[{"data":1,"prerenderedAt":4959},["ShallowReactive",2],{"api-nav":3,"api-guide:\u002Fapi\u002Fauthentication":351,"api-spec:guide:\u002Fapi\u002Fauthentication":4951},[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":19,"body":353,"description":4943,"extension":4944,"meta":4945,"navTitle":19,"navigation":1097,"operations":4946,"order":4947,"path":20,"section":5,"seo":4948,"status":14,"stem":4949,"__hash__":4950},"apiGuides\u002Fapi\u002Fauthentication.md",{"type":354,"value":355,"toc":4912},"minimark",[356,374,379,382,511,514,530,534,541,596,599,626,639,643,649,654,709,785,789,836,918,921,925,935,945,949,1014,1026,1030,1033,1037,1056,1324,1327,1383,1387,1402,1629,1641,1645,1648,1977,2025,2034,2038,2059,2063,2066,2080,2083,2086,2843,2845,2848,3600,3604,3610,3614,3622,3666,3672,3679,3683,3689,3714,3888,3981,3986,4004,4042,4052,4056,4069,4101,4155,4160,4166,4235,4238,4258,4275,4288,4293,4296,4299,4364,4367,4404,4407,4410,4481,4491,4494,4720,4724,4727,4783,4790,4811,4815,4857,4867,4871,4903,4908],[357,358,359,360,364,365,369,370,373],"p",{},"Every route except ",[361,362,363],"code",{},"GET \u002Fv1\u002Fhealth"," needs a token. There is no login endpoint and no OAuth flow: ",[366,367,368],"strong",{},"you sign your own token"," with the secret you were issued, and the API checks the signature. This page covers everything about that token: what it contains, how to sign it in Node.js, PHP and bash, how to reuse it, how to revoke it, and what to check when the API answers ",[361,371,372],{},"401",".",[375,376,378],"h2",{"id":377},"your-credentials","Your credentials",[357,380,381],{},"An operator creates your account and gives you two values, once.",[383,384,385,404],"table",{},[386,387,388],"thead",{},[389,390,391,394,399],"tr",{},[392,393],"th",{},[392,395,396],{},[361,397,398],{},"apiKey",[392,400,401],{},[361,402,403],{},"apiSecret",[405,406,407,419,453,472,486,500],"tbody",{},[389,408,409,413,416],{},[410,411,412],"td",{},"What it is",[410,414,415],{},"Your account's public identifier",[410,417,418],{},"Your signing key",[389,420,421,424,447],{},[410,422,423],{},"Format",[410,425,426,429,430,433,434,433,437,433,440,433,443,446],{},[361,427,428],{},"key_"," followed by exactly 24 characters (",[361,431,432],{},"A-Z",", ",[361,435,436],{},"a-z",[361,438,439],{},"0-9",[361,441,442],{},"-",[361,444,445],{},"_","), 28 characters in total",[410,448,449,452],{},[361,450,451],{},"secret_"," followed by a long random string",[389,454,455,458,469],{},[410,456,457],{},"Where it goes",[410,459,460,461,464,465,468],{},"In every token, as the ",[361,462,463],{},"kid"," header and the ",[361,466,467],{},"sub"," claim",[410,470,471],{},"Nowhere. It never leaves your server. You use it only to compute signatures",[389,473,474,477,480],{},[410,475,476],{},"Secret?",[410,478,479],{},"No. It identifies you; on its own it grants nothing",[410,481,482,485],{},[366,483,484],{},"Yes."," Anyone holding it can act as your account",[389,487,488,491,494],{},[410,489,490],{},"Can you get it again?",[410,492,493],{},"Ask the operator",[410,495,496,499],{},[366,497,498],{},"No."," It is shown once and cannot be recovered",[389,501,502,505,508],{},[410,503,504],{},"Can it be changed?",[410,506,507],{},"No",[410,509,510],{},"No. There is no rotation on an existing account",[357,512,513],{},"The key and the secret are independent random values. You cannot derive one from the other.",[515,516,518],"callout",{"type":517},"security",[357,519,520,521,523,524,529],{},"Keep the ",[361,522,403],{}," in a secret manager or in your server's environment. Never commit it, log it, send it by email or chat, put it in a browser or mobile app, or paste it into a website (online JWT debuggers included). If you think it has leaked, follow ",[525,526,528],"a",{"href":527},"#if-your-secret-leaks","If your secret leaks"," at once.",[375,531,533],{"id":532},"how-a-request-is-authenticated","How a request is authenticated",[357,535,536,537,540],{},"You send the token in the ",[361,538,539],{},"Authorization"," header:",[542,543,548],"pre",{"className":544,"code":545,"language":546,"meta":547,"style":547},"language-http shiki shiki-themes github-light-high-contrast github-dark-high-contrast","GET \u002Fv1\u002Fapi-account\u002Fvalidate-me HTTP\u002F1.1\nHost: api.main-team.org\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleV83ZlF4MkxtTjlwUnRWdzNZekExYkM0ZEUifQ.eyJzdWIiOiJrZXlfN2ZReDJMbU45cFJ0VnczWXpBMWJDNGRFIiwiaWF0IjoxNzg5NDgxNjAwLCJleHAiOjE3ODk0ODUyMDB9.3nQ0k1Q9...\n","http","",[361,549,550,572,586],{"__ignoreMap":547},[551,552,555,558,562,565,568],"span",{"class":553,"line":554},"line",1,[551,556,133],{"class":557},"sHUrx",[551,559,561],{"class":560},"suds8"," \u002Fv1\u002Fapi-account\u002Fvalidate-me ",[551,563,564],{"class":557},"HTTP",[551,566,567],{"class":560},"\u002F",[551,569,571],{"class":570},"s-5SL","1.1\n",[551,573,575,579,582],{"class":553,"line":574},2,[551,576,578],{"class":577},"sne4z","Host",[551,580,581],{"class":557},":",[551,583,585],{"class":584},"sT6z2"," api.main-team.org\n",[551,587,589,591,593],{"class":553,"line":588},3,[551,590,539],{"class":577},[551,592,581],{"class":557},[551,594,595],{"class":584}," Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleV83ZlF4MkxtTjlwUnRWdzNZekExYkM0ZEUifQ.eyJzdWIiOiJrZXlfN2ZReDJMbU45cFJ0VnczWXpBMWJDNGRFIiwiaWF0IjoxNzg5NDgxNjAwLCJleHAiOjE3ODk0ODUyMDB9.3nQ0k1Q9...\n",[357,597,598],{},"The API then:",[600,601,602,612,615,623],"ol",{},[603,604,605,606,608,609,611],"li",{},"reads ",[361,607,463],{}," from the token header and finds the active account with that ",[361,610,398],{},",",[603,613,614],{},"checks the signature with that account's secret, accepting HS256 only,",[603,616,617,618,620,621,611],{},"checks the timestamps and that ",[361,619,467],{}," equals ",[361,622,463],{},[603,624,625],{},"checks that the token has not been revoked.",[357,627,628,629,631,632,634,635,373],{},"If every check passes, the request continues to the permission check (see ",[525,630,25],{"href":26},"). If any check fails, the answer is the same ",[361,633,372],{},", described in ",[525,636,638],{"href":637},"#the-401-checklist","The 401 checklist",[375,640,642],{"id":641},"token-anatomy","Token anatomy",[357,644,645,646,373],{},"A token is a standard JSON Web Token (JWT): three base64url-encoded parts joined by dots, ",[361,647,648],{},"header.payload.signature",[650,651,653],"h3",{"id":652},"header","Header",[542,655,659],{"className":656,"code":657,"language":658,"meta":547,"style":547},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast","{\n  \"alg\": \"HS256\",\n  \"typ\": \"JWT\",\n  \"kid\": \"key_7fQx2LmN9pRtVw3YzA1bC4dE\"\n}\n","json",[361,660,661,666,680,692,703],{"__ignoreMap":547},[551,662,663],{"class":553,"line":554},[551,664,665],{"class":560},"{\n",[551,667,668,671,674,677],{"class":553,"line":574},[551,669,670],{"class":577},"  \"alg\"",[551,672,673],{"class":560},": ",[551,675,676],{"class":584},"\"HS256\"",[551,678,679],{"class":560},",\n",[551,681,682,685,687,690],{"class":553,"line":588},[551,683,684],{"class":577},"  \"typ\"",[551,686,673],{"class":560},[551,688,689],{"class":584},"\"JWT\"",[551,691,679],{"class":560},[551,693,695,698,700],{"class":553,"line":694},4,[551,696,697],{"class":577},"  \"kid\"",[551,699,673],{"class":560},[551,701,702],{"class":584},"\"key_7fQx2LmN9pRtVw3YzA1bC4dE\"\n",[551,704,706],{"class":553,"line":705},5,[551,707,708],{"class":560},"}\n",[383,710,711,724],{},[386,712,713],{},[389,714,715,718,721],{},[392,716,717],{},"Field",[392,719,720],{},"Required",[392,722,723],{},"Rule",[405,725,726,756,770],{},[389,727,728,733,736],{},[410,729,730],{},[361,731,732],{},"alg",[410,734,735],{},"Yes",[410,737,738,739,742,743,433,746,433,749,752,753,373],{},"Must be ",[361,740,741],{},"HS256"," (HMAC with SHA-256). Every other algorithm is refused, including ",[361,744,745],{},"none",[361,747,748],{},"HS384",[361,750,751],{},"HS512"," and ",[361,754,755],{},"RS256",[389,757,758,762,764],{},[410,759,760],{},[361,761,463],{},[410,763,735],{},[410,765,766,767,769],{},"Your ",[361,768,398],{},", exactly as issued. This is how the API knows which account to check the signature against.",[389,771,772,777,779],{},[410,773,774],{},[361,775,776],{},"typ",[410,778,507],{},[410,780,781,784],{},[361,782,783],{},"JWT"," is conventional, and most libraries add it.",[650,786,788],{"id":787},"payload-claims","Payload (claims)",[542,790,792],{"className":656,"code":791,"language":658,"meta":547,"style":547},"{\n  \"sub\": \"key_7fQx2LmN9pRtVw3YzA1bC4dE\",\n  \"iat\": 1789481600,\n  \"exp\": 1789485200\n}\n",[361,793,794,798,810,822,832],{"__ignoreMap":547},[551,795,796],{"class":553,"line":554},[551,797,665],{"class":560},[551,799,800,803,805,808],{"class":553,"line":574},[551,801,802],{"class":577},"  \"sub\"",[551,804,673],{"class":560},[551,806,807],{"class":584},"\"key_7fQx2LmN9pRtVw3YzA1bC4dE\"",[551,809,679],{"class":560},[551,811,812,815,817,820],{"class":553,"line":588},[551,813,814],{"class":577},"  \"iat\"",[551,816,673],{"class":560},[551,818,819],{"class":570},"1789481600",[551,821,679],{"class":560},[551,823,824,827,829],{"class":553,"line":694},[551,825,826],{"class":577},"  \"exp\"",[551,828,673],{"class":560},[551,830,831],{"class":570},"1789485200\n",[551,833,834],{"class":553,"line":705},[551,835,708],{"class":560},[383,837,838,849],{},[386,839,840],{},[389,841,842,845,847],{},[392,843,844],{},"Claim",[392,846,720],{},[392,848,723],{},[405,850,851,867,887,906],{},[389,852,853,857,859],{},[410,854,855],{},[361,856,467],{},[410,858,735],{},[410,860,766,861,863,864,866],{},[361,862,398],{},", the same value as ",[361,865,463],{},". The signed subject has to match the key the token claims to be for.",[389,868,869,874,876],{},[410,870,871],{},[361,872,873],{},"iat",[410,875,735],{},[410,877,878,879,882,883,886],{},"Issued-at time, in ",[366,880,881],{},"whole seconds"," since the Unix epoch (UTC). It may be at most ",[366,884,885],{},"30 seconds"," ahead of the API's clock.",[389,888,889,894,896],{},[410,890,891],{},[361,892,893],{},"exp",[410,895,735],{},[410,897,898,899,901,902,905],{},"Expiry time, in whole seconds since the Unix epoch. It must be later than ",[361,900,873],{}," and at most ",[366,903,904],{},"3600 seconds"," (one hour) after it.",[389,907,908,913,915],{},[410,909,910],{},[361,911,912],{},"nbf",[410,914,507],{},[410,916,917],{},"Optional \"not before\". If present, it is honored, with the same 30 seconds of tolerance.",[357,919,920],{},"Any other claims are ignored. Do not put anything sensitive in the payload: it is only base64url-encoded, and anyone who sees the token can read it.",[650,922,924],{"id":923},"signature","Signature",[357,926,927,928,931,932,934],{},"The signature is HMAC-SHA256 over ",[361,929,930],{},"base64url(header) + \".\" + base64url(payload)",", with your ",[361,933,403],{}," as the key.",[357,936,937,938,941,942,944],{},"Use the secret ",[366,939,940],{},"as text, exactly as issued",": the whole string, ",[361,943,451],{}," prefix included, encoded as UTF-8. Do not base64-decode it, trim parts of it, or add a newline to it. JWT libraries do this correctly when you pass the secret as a plain string.",[650,946,948],{"id":947},"lifetime-rules-at-a-glance","Lifetime rules at a glance",[383,950,951,960],{},[386,952,953],{},[389,954,955,957],{},[392,956,723],{},[392,958,959],{},"Value",[405,961,962,975,987,996,1006],{},[389,963,964,973],{},[410,965,966,967,969,970,972],{},"Longest lifetime (",[361,968,893],{}," − ",[361,971,873],{},")",[410,974,904],{},[389,976,977,980],{},[410,978,979],{},"Shortest lifetime",[410,981,982,984,985],{},[361,983,893],{}," must be later than ",[361,986,873],{},[389,988,989,994],{},[410,990,991,992],{},"Clock tolerance after ",[361,993,893],{},[410,995,885],{},[389,997,998,1004],{},[410,999,1000,1001,1003],{},"How far ",[361,1002,873],{}," may be in the future",[410,1005,885],{},[389,1007,1008,1011],{},[410,1009,1010],{},"Units",[410,1012,1013],{},"Seconds, never milliseconds",[357,1015,1016,1019,1020,1022,1023,1025],{},[366,1017,1018],{},"Why these rules exist."," A token is a bearer credential: whoever holds it can use it until it expires. Requiring ",[361,1021,893],{},", and capping the lifetime at one hour, limits what a leaked token is worth. The cap on a future ",[361,1024,873],{}," stops anyone from minting a token \"for next week\" that would stay valid until then. The 30 seconds of tolerance absorb small clock differences between your servers and ours.",[375,1027,1029],{"id":1028},"sign-a-token","Sign a token",[357,1031,1032],{},"Pick the language you use. Each example produces a token that is valid for 3600 seconds, the maximum.",[650,1034,1036],{"id":1035},"nodejs-with-jsonwebtoken","Node.js with jsonwebtoken",[542,1038,1042],{"className":1039,"code":1040,"language":1041,"meta":547,"style":547},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast","npm install jsonwebtoken\n","bash",[361,1043,1044],{"__ignoreMap":547},[551,1045,1046,1050,1053],{"class":553,"line":554},[551,1047,1049],{"class":1048},"soyes","npm",[551,1051,1052],{"class":584}," install",[551,1054,1055],{"class":584}," jsonwebtoken\n",[542,1057,1061],{"className":1058,"code":1059,"language":1060,"meta":547,"style":547},"language-js shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u002F\u002F token.js\nconst jwt = require('jsonwebtoken');\n\nconst API_KEY = process.env.MTO_API_KEY;       \u002F\u002F key_...\nconst API_SECRET = process.env.MTO_API_SECRET; \u002F\u002F secret_... (the full value)\nconst LIFETIME_SECONDS = 3600;                 \u002F\u002F the maximum the API accepts\n\nfunction mintToken() {\n  const iat = Math.floor(Date.now() \u002F 1000); \u002F\u002F seconds, not milliseconds\n  return jwt.sign(\n    { sub: API_KEY, iat, exp: iat + LIFETIME_SECONDS },\n    API_SECRET,\n    {\n      algorithm: 'HS256',\n      keyid: API_KEY, \u002F\u002F written to the header as \"kid\"\n    },\n  );\n}\n\nmodule.exports = { mintToken };\n","js",[361,1062,1063,1069,1093,1099,1120,1140,1159,1164,1176,1213,1228,1248,1256,1262,1273,1286,1292,1298,1303,1308],{"__ignoreMap":547},[551,1064,1065],{"class":553,"line":554},[551,1066,1068],{"class":1067},"sLBg1","\u002F\u002F token.js\n",[551,1070,1071,1074,1077,1080,1084,1087,1090],{"class":553,"line":574},[551,1072,1073],{"class":557},"const",[551,1075,1076],{"class":570}," jwt",[551,1078,1079],{"class":557}," =",[551,1081,1083],{"class":1082},"sKwhi"," require",[551,1085,1086],{"class":560},"(",[551,1088,1089],{"class":584},"'jsonwebtoken'",[551,1091,1092],{"class":560},");\n",[551,1094,1095],{"class":553,"line":588},[551,1096,1098],{"emptyLinePlaceholder":1097},true,"\n",[551,1100,1101,1103,1106,1108,1111,1114,1117],{"class":553,"line":694},[551,1102,1073],{"class":557},[551,1104,1105],{"class":570}," API_KEY",[551,1107,1079],{"class":557},[551,1109,1110],{"class":560}," process.env.",[551,1112,1113],{"class":570},"MTO_API_KEY",[551,1115,1116],{"class":560},";       ",[551,1118,1119],{"class":1067},"\u002F\u002F key_...\n",[551,1121,1122,1124,1127,1129,1131,1134,1137],{"class":553,"line":705},[551,1123,1073],{"class":557},[551,1125,1126],{"class":570}," API_SECRET",[551,1128,1079],{"class":557},[551,1130,1110],{"class":560},[551,1132,1133],{"class":570},"MTO_API_SECRET",[551,1135,1136],{"class":560},"; ",[551,1138,1139],{"class":1067},"\u002F\u002F secret_... (the full value)\n",[551,1141,1143,1145,1148,1150,1153,1156],{"class":553,"line":1142},6,[551,1144,1073],{"class":557},[551,1146,1147],{"class":570}," LIFETIME_SECONDS",[551,1149,1079],{"class":557},[551,1151,1152],{"class":570}," 3600",[551,1154,1155],{"class":560},";                 ",[551,1157,1158],{"class":1067},"\u002F\u002F the maximum the API accepts\n",[551,1160,1162],{"class":553,"line":1161},7,[551,1163,1098],{"emptyLinePlaceholder":1097},[551,1165,1167,1170,1173],{"class":553,"line":1166},8,[551,1168,1169],{"class":557},"function",[551,1171,1172],{"class":1082}," mintToken",[551,1174,1175],{"class":560},"() {\n",[551,1177,1179,1182,1185,1187,1190,1193,1196,1199,1202,1204,1207,1210],{"class":553,"line":1178},9,[551,1180,1181],{"class":557},"  const",[551,1183,1184],{"class":570}," iat",[551,1186,1079],{"class":557},[551,1188,1189],{"class":560}," Math.",[551,1191,1192],{"class":1082},"floor",[551,1194,1195],{"class":560},"(Date.",[551,1197,1198],{"class":1082},"now",[551,1200,1201],{"class":560},"() ",[551,1203,567],{"class":557},[551,1205,1206],{"class":570}," 1000",[551,1208,1209],{"class":560},"); ",[551,1211,1212],{"class":1067},"\u002F\u002F seconds, not milliseconds\n",[551,1214,1216,1219,1222,1225],{"class":553,"line":1215},10,[551,1217,1218],{"class":557},"  return",[551,1220,1221],{"class":560}," jwt.",[551,1223,1224],{"class":1082},"sign",[551,1226,1227],{"class":560},"(\n",[551,1229,1231,1234,1237,1240,1243,1245],{"class":553,"line":1230},11,[551,1232,1233],{"class":560},"    { sub: ",[551,1235,1236],{"class":570},"API_KEY",[551,1238,1239],{"class":560},", iat, exp: iat ",[551,1241,1242],{"class":557},"+",[551,1244,1147],{"class":570},[551,1246,1247],{"class":560}," },\n",[551,1249,1251,1254],{"class":553,"line":1250},12,[551,1252,1253],{"class":570},"    API_SECRET",[551,1255,679],{"class":560},[551,1257,1259],{"class":553,"line":1258},13,[551,1260,1261],{"class":560},"    {\n",[551,1263,1265,1268,1271],{"class":553,"line":1264},14,[551,1266,1267],{"class":560},"      algorithm: ",[551,1269,1270],{"class":584},"'HS256'",[551,1272,679],{"class":560},[551,1274,1276,1279,1281,1283],{"class":553,"line":1275},15,[551,1277,1278],{"class":560},"      keyid: ",[551,1280,1236],{"class":570},[551,1282,433],{"class":560},[551,1284,1285],{"class":1067},"\u002F\u002F written to the header as \"kid\"\n",[551,1287,1289],{"class":553,"line":1288},16,[551,1290,1291],{"class":560},"    },\n",[551,1293,1295],{"class":553,"line":1294},17,[551,1296,1297],{"class":560},"  );\n",[551,1299,1301],{"class":553,"line":1300},18,[551,1302,708],{"class":560},[551,1304,1306],{"class":553,"line":1305},19,[551,1307,1098],{"emptyLinePlaceholder":1097},[551,1309,1311,1314,1316,1319,1321],{"class":553,"line":1310},20,[551,1312,1313],{"class":570},"module",[551,1315,373],{"class":560},[551,1317,1318],{"class":570},"exports",[551,1320,1079],{"class":557},[551,1322,1323],{"class":560}," { mintToken };\n",[357,1325,1326],{},"Notes for jsonwebtoken:",[1328,1329,1330,1351,1367],"ul",{},[603,1331,1332,1333,752,1335,1337,1338,1341,1342,1345,1346,1348,1349,373],{},"Put ",[361,1334,873],{},[361,1336,893],{}," in the payload ",[366,1339,1340],{},"or"," use the ",[361,1343,1344],{},"expiresIn"," option, not both. The library refuses a payload ",[361,1347,893],{}," combined with ",[361,1350,1344],{},[603,1352,1353,1354,1356,1357,1360,1361,1364,1365,373],{},"The library adds ",[361,1355,873],{}," automatically unless you pass ",[361,1358,1359],{},"noTimestamp: true",". Never pass ",[361,1362,1363],{},"noTimestamp",": the API requires ",[361,1366,873],{},[603,1368,1369,1370,1373,1374,1376,1377,1379,1380,373],{},"With another Node library, such as ",[361,1371,1372],{},"jose",", check that it sets ",[361,1375,873],{},". In ",[361,1378,1372],{}," that means calling ",[361,1381,1382],{},".setIssuedAt()",[650,1384,1386],{"id":1385},"php-with-firebasephp-jwt","PHP with firebase\u002Fphp-jwt",[542,1388,1390],{"className":1039,"code":1389,"language":1041,"meta":547,"style":547},"composer require firebase\u002Fphp-jwt\n",[361,1391,1392],{"__ignoreMap":547},[551,1393,1394,1397,1399],{"class":553,"line":554},[551,1395,1396],{"class":1048},"composer",[551,1398,1083],{"class":584},[551,1400,1401],{"class":584}," firebase\u002Fphp-jwt\n",[542,1403,1407],{"className":1404,"code":1405,"language":1406,"meta":547,"style":547},"language-php shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u003C?php\n\u002F\u002F Token.php\nrequire __DIR__ . '\u002Fvendor\u002Fautoload.php';\n\nuse Firebase\\JWT\\JWT;\n\nfunction mintToken(string $apiKey, string $apiSecret, int $lifetime = 3600): string\n{\n    $iat = time(); \u002F\u002F seconds\n    return JWT::encode(\n        ['sub' => $apiKey, 'iat' => $iat, 'exp' => $iat + $lifetime],\n        $apiSecret,\n        'HS256',\n        $apiKey \u002F\u002F $keyId: written to the header as \"kid\"\n    );\n}\n\n$token = mintToken(getenv('MTO_API_KEY'), getenv('MTO_API_SECRET'));\n","php",[361,1408,1409,1417,1422,1439,1443,1453,1457,1494,1498,1514,1530,1564,1569,1576,1584,1589,1593,1597],{"__ignoreMap":547},[551,1410,1411,1414],{"class":553,"line":554},[551,1412,1413],{"class":557},"\u003C?",[551,1415,1416],{"class":570},"php\n",[551,1418,1419],{"class":553,"line":574},[551,1420,1421],{"class":1067},"\u002F\u002F Token.php\n",[551,1423,1424,1427,1430,1433,1436],{"class":553,"line":588},[551,1425,1426],{"class":557},"require",[551,1428,1429],{"class":570}," __DIR__",[551,1431,1432],{"class":557}," .",[551,1434,1435],{"class":584}," '\u002Fvendor\u002Fautoload.php'",[551,1437,1438],{"class":560},";\n",[551,1440,1441],{"class":553,"line":694},[551,1442,1098],{"emptyLinePlaceholder":1097},[551,1444,1445,1448,1451],{"class":553,"line":705},[551,1446,1447],{"class":557},"use",[551,1449,1450],{"class":570}," Firebase\\JWT\\JWT",[551,1452,1438],{"class":560},[551,1454,1455],{"class":553,"line":1142},[551,1456,1098],{"emptyLinePlaceholder":1097},[551,1458,1459,1461,1463,1465,1468,1471,1473,1476,1479,1482,1485,1487,1489,1491],{"class":553,"line":1161},[551,1460,1169],{"class":557},[551,1462,1172],{"class":1082},[551,1464,1086],{"class":560},[551,1466,1467],{"class":557},"string",[551,1469,1470],{"class":560}," $apiKey, ",[551,1472,1467],{"class":557},[551,1474,1475],{"class":560}," $apiSecret, ",[551,1477,1478],{"class":557},"int",[551,1480,1481],{"class":560}," $lifetime ",[551,1483,1484],{"class":557},"=",[551,1486,1152],{"class":570},[551,1488,972],{"class":560},[551,1490,581],{"class":557},[551,1492,1493],{"class":557}," string\n",[551,1495,1496],{"class":553,"line":1166},[551,1497,665],{"class":560},[551,1499,1500,1503,1505,1508,1511],{"class":553,"line":1178},[551,1501,1502],{"class":560},"    $iat ",[551,1504,1484],{"class":557},[551,1506,1507],{"class":570}," time",[551,1509,1510],{"class":560},"(); ",[551,1512,1513],{"class":1067},"\u002F\u002F seconds\n",[551,1515,1516,1519,1522,1525,1528],{"class":553,"line":1215},[551,1517,1518],{"class":557},"    return",[551,1520,1521],{"class":570}," JWT",[551,1523,1524],{"class":557},"::",[551,1526,1527],{"class":1082},"encode",[551,1529,1227],{"class":560},[551,1531,1532,1535,1538,1541,1543,1546,1548,1551,1554,1556,1559,1561],{"class":553,"line":1230},[551,1533,1534],{"class":560},"        [",[551,1536,1537],{"class":584},"'sub'",[551,1539,1540],{"class":557}," =>",[551,1542,1470],{"class":560},[551,1544,1545],{"class":584},"'iat'",[551,1547,1540],{"class":557},[551,1549,1550],{"class":560}," $iat, ",[551,1552,1553],{"class":584},"'exp'",[551,1555,1540],{"class":557},[551,1557,1558],{"class":560}," $iat ",[551,1560,1242],{"class":557},[551,1562,1563],{"class":560}," $lifetime],\n",[551,1565,1566],{"class":553,"line":1250},[551,1567,1568],{"class":560},"        $apiSecret,\n",[551,1570,1571,1574],{"class":553,"line":1258},[551,1572,1573],{"class":584},"        'HS256'",[551,1575,679],{"class":560},[551,1577,1578,1581],{"class":553,"line":1264},[551,1579,1580],{"class":560},"        $apiKey ",[551,1582,1583],{"class":1067},"\u002F\u002F $keyId: written to the header as \"kid\"\n",[551,1585,1586],{"class":553,"line":1275},[551,1587,1588],{"class":560},"    );\n",[551,1590,1591],{"class":553,"line":1288},[551,1592,708],{"class":560},[551,1594,1595],{"class":553,"line":1294},[551,1596,1098],{"emptyLinePlaceholder":1097},[551,1598,1599,1602,1604,1606,1608,1611,1613,1616,1619,1621,1623,1626],{"class":553,"line":1300},[551,1600,1601],{"class":560},"$token ",[551,1603,1484],{"class":557},[551,1605,1172],{"class":1082},[551,1607,1086],{"class":560},[551,1609,1610],{"class":570},"getenv",[551,1612,1086],{"class":560},[551,1614,1615],{"class":584},"'MTO_API_KEY'",[551,1617,1618],{"class":560},"), ",[551,1620,1610],{"class":570},[551,1622,1086],{"class":560},[551,1624,1625],{"class":584},"'MTO_API_SECRET'",[551,1627,1628],{"class":560},"));\n",[357,1630,1631,1634,1635,1637,1638,1640],{},[361,1632,1633],{},"JWT::encode($payload, $key, $alg, $keyId)"," sets ",[361,1636,463],{}," from its fourth argument. If you leave it out, the token has no ",[361,1639,463],{}," and the API refuses it.",[650,1642,1644],{"id":1643},"bash-with-openssl","bash with openssl",[357,1646,1647],{},"Useful for a quick test from a terminal, or in a shell script where you cannot install a library.",[542,1649,1651],{"className":1039,"code":1650,"language":1041,"meta":547,"style":547},"#!\u002Fusr\u002Fbin\u002Fenv bash\n# mint-token.sh: prints a token for MTO_API_KEY \u002F MTO_API_SECRET\nset -euo pipefail\n: \"${MTO_API_KEY:?set MTO_API_KEY}\"\n: \"${MTO_API_SECRET:?set MTO_API_SECRET}\"\n\nlifetime=${1:-3600}\n\n# base64url without padding, as JWT requires\nb64url() { openssl base64 -e -A | tr '+\u002F' '-_' | tr -d '='; }\n\nnow=$(date +%s)\nheader=$(printf '{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"%s\"}' \"$MTO_API_KEY\" | b64url)\npayload=$(printf '{\"sub\":\"%s\",\"iat\":%d,\"exp\":%d}' \\\n  \"$MTO_API_KEY\" \"$now\" \"$((now + lifetime))\" | b64url)\nsignature=$(printf '%s.%s' \"$header\" \"$payload\" \\\n  | openssl dgst -sha256 -hmac \"$MTO_API_SECRET\" -binary | b64url)\n\nprintf '%s.%s.%s\\n' \"$header\" \"$payload\" \"$signature\"\n",[361,1652,1653,1658,1663,1674,1694,1711,1715,1731,1735,1740,1785,1789,1807,1837,1854,1884,1913,1946,1950],{"__ignoreMap":547},[551,1654,1655],{"class":553,"line":554},[551,1656,1657],{"class":1067},"#!\u002Fusr\u002Fbin\u002Fenv bash\n",[551,1659,1660],{"class":553,"line":574},[551,1661,1662],{"class":1067},"# mint-token.sh: prints a token for MTO_API_KEY \u002F MTO_API_SECRET\n",[551,1664,1665,1668,1671],{"class":553,"line":588},[551,1666,1667],{"class":570},"set",[551,1669,1670],{"class":570}," -euo",[551,1672,1673],{"class":584}," pipefail\n",[551,1675,1676,1678,1681,1683,1686,1688,1691],{"class":553,"line":694},[551,1677,581],{"class":570},[551,1679,1680],{"class":584}," \"${",[551,1682,1113],{"class":560},[551,1684,1685],{"class":557},":?",[551,1687,1667],{"class":560},[551,1689,1690],{"class":560}," MTO_API_KEY",[551,1692,1693],{"class":584},"}\"\n",[551,1695,1696,1698,1700,1702,1704,1706,1709],{"class":553,"line":705},[551,1697,581],{"class":570},[551,1699,1680],{"class":584},[551,1701,1133],{"class":560},[551,1703,1685],{"class":557},[551,1705,1667],{"class":560},[551,1707,1708],{"class":560}," MTO_API_SECRET",[551,1710,1693],{"class":584},[551,1712,1713],{"class":553,"line":1142},[551,1714,1098],{"emptyLinePlaceholder":1097},[551,1716,1717,1720,1722,1725,1728],{"class":553,"line":1161},[551,1718,1719],{"class":560},"lifetime",[551,1721,1484],{"class":557},[551,1723,1724],{"class":1048},"${1",[551,1726,1727],{"class":557},":-",[551,1729,1730],{"class":1048},"3600}\n",[551,1732,1733],{"class":553,"line":1166},[551,1734,1098],{"emptyLinePlaceholder":1097},[551,1736,1737],{"class":553,"line":1178},[551,1738,1739],{"class":1067},"# base64url without padding, as JWT requires\n",[551,1741,1742,1745,1748,1751,1754,1757,1760,1763,1766,1769,1772,1774,1776,1779,1782],{"class":553,"line":1215},[551,1743,1744],{"class":1082},"b64url",[551,1746,1747],{"class":560},"() { ",[551,1749,1750],{"class":1048},"openssl",[551,1752,1753],{"class":584}," base64",[551,1755,1756],{"class":570}," -e",[551,1758,1759],{"class":570}," -A",[551,1761,1762],{"class":557}," |",[551,1764,1765],{"class":1048}," tr",[551,1767,1768],{"class":584}," '+\u002F'",[551,1770,1771],{"class":584}," '-_'",[551,1773,1762],{"class":557},[551,1775,1765],{"class":1048},[551,1777,1778],{"class":570}," -d",[551,1780,1781],{"class":584}," '='",[551,1783,1784],{"class":560},"; }\n",[551,1786,1787],{"class":553,"line":1230},[551,1788,1098],{"emptyLinePlaceholder":1097},[551,1790,1791,1793,1795,1798,1801,1804],{"class":553,"line":1250},[551,1792,1198],{"class":560},[551,1794,1484],{"class":557},[551,1796,1797],{"class":560},"$(",[551,1799,1800],{"class":1048},"date",[551,1802,1803],{"class":584}," +%s",[551,1805,1806],{"class":560},")\n",[551,1808,1809,1811,1813,1815,1818,1821,1824,1827,1830,1832,1835],{"class":553,"line":1258},[551,1810,652],{"class":560},[551,1812,1484],{"class":557},[551,1814,1797],{"class":560},[551,1816,1817],{"class":570},"printf",[551,1819,1820],{"class":584}," '{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"%s\"}'",[551,1822,1823],{"class":584}," \"",[551,1825,1826],{"class":560},"$MTO_API_KEY",[551,1828,1829],{"class":584},"\"",[551,1831,1762],{"class":557},[551,1833,1834],{"class":1048}," b64url",[551,1836,1806],{"class":560},[551,1838,1839,1842,1844,1846,1848,1851],{"class":553,"line":1264},[551,1840,1841],{"class":560},"payload",[551,1843,1484],{"class":557},[551,1845,1797],{"class":560},[551,1847,1817],{"class":570},[551,1849,1850],{"class":584}," '{\"sub\":\"%s\",\"iat\":%d,\"exp\":%d}'",[551,1852,1853],{"class":557}," \\\n",[551,1855,1856,1859,1861,1863,1865,1868,1870,1873,1875,1878,1880,1882],{"class":553,"line":1275},[551,1857,1858],{"class":584},"  \"",[551,1860,1826],{"class":560},[551,1862,1829],{"class":584},[551,1864,1823],{"class":584},[551,1866,1867],{"class":560},"$now",[551,1869,1829],{"class":584},[551,1871,1872],{"class":584}," \"$((",[551,1874,1198],{"class":1048},[551,1876,1877],{"class":584}," + lifetime))\"",[551,1879,1762],{"class":557},[551,1881,1834],{"class":1048},[551,1883,1806],{"class":560},[551,1885,1886,1888,1890,1892,1894,1897,1899,1902,1904,1906,1909,1911],{"class":553,"line":1288},[551,1887,923],{"class":560},[551,1889,1484],{"class":557},[551,1891,1797],{"class":560},[551,1893,1817],{"class":570},[551,1895,1896],{"class":584}," '%s.%s'",[551,1898,1823],{"class":584},[551,1900,1901],{"class":560},"$header",[551,1903,1829],{"class":584},[551,1905,1823],{"class":584},[551,1907,1908],{"class":560},"$payload",[551,1910,1829],{"class":584},[551,1912,1853],{"class":557},[551,1914,1915,1918,1921,1924,1927,1930,1932,1935,1937,1940,1942,1944],{"class":553,"line":1294},[551,1916,1917],{"class":557},"  |",[551,1919,1920],{"class":1048}," openssl",[551,1922,1923],{"class":584}," dgst",[551,1925,1926],{"class":570}," -sha256",[551,1928,1929],{"class":570}," -hmac",[551,1931,1823],{"class":584},[551,1933,1934],{"class":560},"$MTO_API_SECRET",[551,1936,1829],{"class":584},[551,1938,1939],{"class":570}," -binary",[551,1941,1762],{"class":557},[551,1943,1834],{"class":1048},[551,1945,1806],{"class":560},[551,1947,1948],{"class":553,"line":1300},[551,1949,1098],{"emptyLinePlaceholder":1097},[551,1951,1952,1954,1957,1959,1961,1963,1965,1967,1969,1971,1974],{"class":553,"line":1305},[551,1953,1817],{"class":570},[551,1955,1956],{"class":584}," '%s.%s.%s\\n'",[551,1958,1823],{"class":584},[551,1960,1901],{"class":560},[551,1962,1829],{"class":584},[551,1964,1823],{"class":584},[551,1966,1908],{"class":560},[551,1968,1829],{"class":584},[551,1970,1823],{"class":584},[551,1972,1973],{"class":560},"$signature",[551,1975,1976],{"class":584},"\"\n",[542,1978,1980],{"className":1039,"code":1979,"language":1041,"meta":547,"style":547},"export TOKEN=$(.\u002Fmint-token.sh)\ncurl -s https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Fvalidate-me \\\n  -H \"Authorization: Bearer $TOKEN\"\n",[361,1981,1982,1999,2012],{"__ignoreMap":547},[551,1983,1984,1987,1990,1992,1994,1997],{"class":553,"line":554},[551,1985,1986],{"class":557},"export",[551,1988,1989],{"class":560}," TOKEN",[551,1991,1484],{"class":557},[551,1993,1797],{"class":560},[551,1995,1996],{"class":1048},".\u002Fmint-token.sh",[551,1998,1806],{"class":560},[551,2000,2001,2004,2007,2010],{"class":553,"line":574},[551,2002,2003],{"class":1048},"curl",[551,2005,2006],{"class":570}," -s",[551,2008,2009],{"class":584}," https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Fvalidate-me",[551,2011,1853],{"class":557},[551,2013,2014,2017,2020,2023],{"class":553,"line":588},[551,2015,2016],{"class":570},"  -H",[551,2018,2019],{"class":584}," \"Authorization: Bearer ",[551,2021,2022],{"class":560},"$TOKEN",[551,2024,1976],{"class":584},[515,2026,2028],{"type":2027},"warn",[357,2029,2030,2033],{},[361,2031,2032],{},"openssl dgst -hmac"," takes the secret as a command-line argument, and on a shared machine other users can see command lines in the process list. Use this script on your own workstation or a single-tenant server, and use a JWT library in production code.",[650,2035,2037],{"id":2036},"any-other-language","Any other language",[357,2039,2040,2041,2043,2044,2046,2047,2049,2050,752,2052,2054,2055,2058],{},"Any JWT library that supports HS256 and lets you set the ",[361,2042,463],{}," header works. There are no key pairs, certificates or PEM files. Check three things in your library: it signs with HS256, it writes ",[361,2045,463],{}," into the ",[366,2048,652],{}," (not the payload), and it writes ",[361,2051,873],{},[361,2053,893],{}," in seconds. ",[525,2056,2057],{"href":315},"Build your own client"," lists everything else a client needs.",[375,2060,2062],{"id":2061},"reuse-tokens-the-cache-pattern","Reuse tokens: the cache pattern",[357,2064,2065],{},"You can sign a new token for every request; signing is cheap. But reusing one token until shortly before it expires is simpler to debug, and it means one fewer thing can go wrong on every call. The pattern:",[600,2067,2068,2074,2077],{},[603,2069,2070,2071,2073],{},"Keep the current token and its ",[361,2072,893],{}," in memory.",[603,2075,2076],{},"Before each request, if the token expires within the next 60 seconds, sign a new one.",[603,2078,2079],{},"Otherwise reuse it.",[357,2081,2082],{},"A 60-second margin makes sure a token never expires while a request is in flight, and absorbs small clock differences.",[650,2084,308],{"id":2085},"nodejs",[542,2087,2089],{"className":1058,"code":2088,"language":1060,"meta":547,"style":547},"\u002F\u002F token-cache.js\nconst jwt = require('jsonwebtoken');\n\nconst API_KEY = process.env.MTO_API_KEY;\nconst API_SECRET = process.env.MTO_API_SECRET;\nconst LIFETIME_SECONDS = 3600;\nconst REFRESH_MARGIN_SECONDS = 60;\n\nlet current = null; \u002F\u002F { token, exp }\n\nfunction getToken() {\n  const now = Math.floor(Date.now() \u002F 1000);\n  if (current && current.exp - REFRESH_MARGIN_SECONDS > now) {\n    return current.token;\n  }\n  const iat = now;\n  const exp = iat + LIFETIME_SECONDS;\n  const token = jwt.sign({ sub: API_KEY, iat, exp }, API_SECRET, {\n    algorithm: 'HS256',\n    keyid: API_KEY,\n  });\n  current = { token, exp };\n  return token;\n}\n\nasync function api(path, { method = 'GET', body } = {}) {\n  const res = await fetch(`https:\u002F\u002Fapi.main-team.org\u002Fv1${path}`, {\n    method,\n    headers: {\n      Authorization: `Bearer ${getToken()}`,\n      ...(body !== undefined ? { 'Content-Type': 'application\u002Fjson' } : {}),\n    },\n    body: body !== undefined ? JSON.stringify(body) : undefined,\n  });\n  const requestId = res.headers.get('x-request-id');\n  \u002F\u002F Error bodies are JSON too, but a proxy in between can answer with HTML.\n  const text = await res.text();\n  let payload = null;\n  try {\n    payload = text ? JSON.parse(text) : null;\n  } catch {\n    payload = null;\n  }\n  if (!res.ok) {\n    const err = new Error(`${res.status} ${payload?.error?.code}: ${payload?.error?.message}`);\n    err.status = res.status;\n    err.code = payload?.error?.code;\n    err.requestId = payload?.error?.request_id ?? requestId;\n    throw err;\n  }\n  return payload;\n}\n\nmodule.exports = { getToken, api };\n",[361,2090,2091,2096,2112,2116,2130,2144,2156,2170,2174,2192,2196,2205,2230,2254,2261,2266,2277,2295,2322,2331,2340,2346,2357,2365,2370,2375,2416,2444,2450,2456,2475,2511,2516,2545,2550,2573,2579,2600,2615,2624,2654,2665,2676,2681,2695,2758,2769,2780,2797,2806,2811,2819,2824,2829],{"__ignoreMap":547},[551,2092,2093],{"class":553,"line":554},[551,2094,2095],{"class":1067},"\u002F\u002F token-cache.js\n",[551,2097,2098,2100,2102,2104,2106,2108,2110],{"class":553,"line":574},[551,2099,1073],{"class":557},[551,2101,1076],{"class":570},[551,2103,1079],{"class":557},[551,2105,1083],{"class":1082},[551,2107,1086],{"class":560},[551,2109,1089],{"class":584},[551,2111,1092],{"class":560},[551,2113,2114],{"class":553,"line":588},[551,2115,1098],{"emptyLinePlaceholder":1097},[551,2117,2118,2120,2122,2124,2126,2128],{"class":553,"line":694},[551,2119,1073],{"class":557},[551,2121,1105],{"class":570},[551,2123,1079],{"class":557},[551,2125,1110],{"class":560},[551,2127,1113],{"class":570},[551,2129,1438],{"class":560},[551,2131,2132,2134,2136,2138,2140,2142],{"class":553,"line":705},[551,2133,1073],{"class":557},[551,2135,1126],{"class":570},[551,2137,1079],{"class":557},[551,2139,1110],{"class":560},[551,2141,1133],{"class":570},[551,2143,1438],{"class":560},[551,2145,2146,2148,2150,2152,2154],{"class":553,"line":1142},[551,2147,1073],{"class":557},[551,2149,1147],{"class":570},[551,2151,1079],{"class":557},[551,2153,1152],{"class":570},[551,2155,1438],{"class":560},[551,2157,2158,2160,2163,2165,2168],{"class":553,"line":1161},[551,2159,1073],{"class":557},[551,2161,2162],{"class":570}," REFRESH_MARGIN_SECONDS",[551,2164,1079],{"class":557},[551,2166,2167],{"class":570}," 60",[551,2169,1438],{"class":560},[551,2171,2172],{"class":553,"line":1166},[551,2173,1098],{"emptyLinePlaceholder":1097},[551,2175,2176,2179,2182,2184,2187,2189],{"class":553,"line":1178},[551,2177,2178],{"class":557},"let",[551,2180,2181],{"class":560}," current ",[551,2183,1484],{"class":557},[551,2185,2186],{"class":570}," null",[551,2188,1136],{"class":560},[551,2190,2191],{"class":1067},"\u002F\u002F { token, exp }\n",[551,2193,2194],{"class":553,"line":1215},[551,2195,1098],{"emptyLinePlaceholder":1097},[551,2197,2198,2200,2203],{"class":553,"line":1230},[551,2199,1169],{"class":557},[551,2201,2202],{"class":1082}," getToken",[551,2204,1175],{"class":560},[551,2206,2207,2209,2212,2214,2216,2218,2220,2222,2224,2226,2228],{"class":553,"line":1250},[551,2208,1181],{"class":557},[551,2210,2211],{"class":570}," now",[551,2213,1079],{"class":557},[551,2215,1189],{"class":560},[551,2217,1192],{"class":1082},[551,2219,1195],{"class":560},[551,2221,1198],{"class":1082},[551,2223,1201],{"class":560},[551,2225,567],{"class":557},[551,2227,1206],{"class":570},[551,2229,1092],{"class":560},[551,2231,2232,2235,2238,2241,2244,2246,2248,2251],{"class":553,"line":1258},[551,2233,2234],{"class":557},"  if",[551,2236,2237],{"class":560}," (current ",[551,2239,2240],{"class":557},"&&",[551,2242,2243],{"class":560}," current.exp ",[551,2245,442],{"class":557},[551,2247,2162],{"class":570},[551,2249,2250],{"class":557}," >",[551,2252,2253],{"class":560}," now) {\n",[551,2255,2256,2258],{"class":553,"line":1264},[551,2257,1518],{"class":557},[551,2259,2260],{"class":560}," current.token;\n",[551,2262,2263],{"class":553,"line":1275},[551,2264,2265],{"class":560},"  }\n",[551,2267,2268,2270,2272,2274],{"class":553,"line":1288},[551,2269,1181],{"class":557},[551,2271,1184],{"class":570},[551,2273,1079],{"class":557},[551,2275,2276],{"class":560}," now;\n",[551,2278,2279,2281,2284,2286,2289,2291,2293],{"class":553,"line":1294},[551,2280,1181],{"class":557},[551,2282,2283],{"class":570}," exp",[551,2285,1079],{"class":557},[551,2287,2288],{"class":560}," iat ",[551,2290,1242],{"class":557},[551,2292,1147],{"class":570},[551,2294,1438],{"class":560},[551,2296,2297,2299,2302,2304,2306,2308,2311,2313,2316,2319],{"class":553,"line":1300},[551,2298,1181],{"class":557},[551,2300,2301],{"class":570}," token",[551,2303,1079],{"class":557},[551,2305,1221],{"class":560},[551,2307,1224],{"class":1082},[551,2309,2310],{"class":560},"({ sub: ",[551,2312,1236],{"class":570},[551,2314,2315],{"class":560},", iat, exp }, ",[551,2317,2318],{"class":570},"API_SECRET",[551,2320,2321],{"class":560},", {\n",[551,2323,2324,2327,2329],{"class":553,"line":1305},[551,2325,2326],{"class":560},"    algorithm: ",[551,2328,1270],{"class":584},[551,2330,679],{"class":560},[551,2332,2333,2336,2338],{"class":553,"line":1310},[551,2334,2335],{"class":560},"    keyid: ",[551,2337,1236],{"class":570},[551,2339,679],{"class":560},[551,2341,2343],{"class":553,"line":2342},21,[551,2344,2345],{"class":560},"  });\n",[551,2347,2349,2352,2354],{"class":553,"line":2348},22,[551,2350,2351],{"class":560},"  current ",[551,2353,1484],{"class":557},[551,2355,2356],{"class":560}," { token, exp };\n",[551,2358,2360,2362],{"class":553,"line":2359},23,[551,2361,1218],{"class":557},[551,2363,2364],{"class":560}," token;\n",[551,2366,2368],{"class":553,"line":2367},24,[551,2369,708],{"class":560},[551,2371,2373],{"class":553,"line":2372},25,[551,2374,1098],{"emptyLinePlaceholder":1097},[551,2376,2378,2381,2384,2387,2389,2392,2395,2398,2400,2403,2405,2408,2411,2413],{"class":553,"line":2377},26,[551,2379,2380],{"class":557},"async",[551,2382,2383],{"class":557}," function",[551,2385,2386],{"class":1082}," api",[551,2388,1086],{"class":560},[551,2390,2391],{"class":1048},"path",[551,2393,2394],{"class":560},", { ",[551,2396,2397],{"class":1048},"method",[551,2399,1079],{"class":557},[551,2401,2402],{"class":584}," 'GET'",[551,2404,433],{"class":560},[551,2406,2407],{"class":1048},"body",[551,2409,2410],{"class":560}," } ",[551,2412,1484],{"class":557},[551,2414,2415],{"class":560}," {}) {\n",[551,2417,2419,2421,2424,2426,2429,2432,2434,2437,2439,2442],{"class":553,"line":2418},27,[551,2420,1181],{"class":557},[551,2422,2423],{"class":570}," res",[551,2425,1079],{"class":557},[551,2427,2428],{"class":557}," await",[551,2430,2431],{"class":1082}," fetch",[551,2433,1086],{"class":560},[551,2435,2436],{"class":584},"`https:\u002F\u002Fapi.main-team.org\u002Fv1${",[551,2438,2391],{"class":560},[551,2440,2441],{"class":584},"}`",[551,2443,2321],{"class":560},[551,2445,2447],{"class":553,"line":2446},28,[551,2448,2449],{"class":560},"    method,\n",[551,2451,2453],{"class":553,"line":2452},29,[551,2454,2455],{"class":560},"    headers: {\n",[551,2457,2459,2462,2465,2468,2471,2473],{"class":553,"line":2458},30,[551,2460,2461],{"class":560},"      Authorization: ",[551,2463,2464],{"class":584},"`Bearer ${",[551,2466,2467],{"class":1082},"getToken",[551,2469,2470],{"class":584},"()",[551,2472,2441],{"class":584},[551,2474,679],{"class":560},[551,2476,2478,2481,2484,2487,2490,2493,2496,2499,2501,2504,2506,2508],{"class":553,"line":2477},31,[551,2479,2480],{"class":557},"      ...",[551,2482,2483],{"class":560},"(body ",[551,2485,2486],{"class":557},"!==",[551,2488,2489],{"class":570}," undefined",[551,2491,2492],{"class":557}," ?",[551,2494,2495],{"class":560}," { ",[551,2497,2498],{"class":584},"'Content-Type'",[551,2500,673],{"class":560},[551,2502,2503],{"class":584},"'application\u002Fjson'",[551,2505,2410],{"class":560},[551,2507,581],{"class":557},[551,2509,2510],{"class":560}," {}),\n",[551,2512,2514],{"class":553,"line":2513},32,[551,2515,1291],{"class":560},[551,2517,2519,2522,2524,2526,2528,2531,2533,2536,2539,2541,2543],{"class":553,"line":2518},33,[551,2520,2521],{"class":560},"    body: body ",[551,2523,2486],{"class":557},[551,2525,2489],{"class":570},[551,2527,2492],{"class":557},[551,2529,2530],{"class":570}," JSON",[551,2532,373],{"class":560},[551,2534,2535],{"class":1082},"stringify",[551,2537,2538],{"class":560},"(body) ",[551,2540,581],{"class":557},[551,2542,2489],{"class":570},[551,2544,679],{"class":560},[551,2546,2548],{"class":553,"line":2547},34,[551,2549,2345],{"class":560},[551,2551,2553,2555,2558,2560,2563,2566,2568,2571],{"class":553,"line":2552},35,[551,2554,1181],{"class":557},[551,2556,2557],{"class":570}," requestId",[551,2559,1079],{"class":557},[551,2561,2562],{"class":560}," res.headers.",[551,2564,2565],{"class":1082},"get",[551,2567,1086],{"class":560},[551,2569,2570],{"class":584},"'x-request-id'",[551,2572,1092],{"class":560},[551,2574,2576],{"class":553,"line":2575},36,[551,2577,2578],{"class":1067},"  \u002F\u002F Error bodies are JSON too, but a proxy in between can answer with HTML.\n",[551,2580,2582,2584,2587,2589,2591,2594,2597],{"class":553,"line":2581},37,[551,2583,1181],{"class":557},[551,2585,2586],{"class":570}," text",[551,2588,1079],{"class":557},[551,2590,2428],{"class":557},[551,2592,2593],{"class":560}," res.",[551,2595,2596],{"class":1082},"text",[551,2598,2599],{"class":560},"();\n",[551,2601,2603,2606,2609,2611,2613],{"class":553,"line":2602},38,[551,2604,2605],{"class":557},"  let",[551,2607,2608],{"class":560}," payload ",[551,2610,1484],{"class":557},[551,2612,2186],{"class":570},[551,2614,1438],{"class":560},[551,2616,2618,2621],{"class":553,"line":2617},39,[551,2619,2620],{"class":557},"  try",[551,2622,2623],{"class":560}," {\n",[551,2625,2627,2630,2632,2635,2638,2640,2642,2645,2648,2650,2652],{"class":553,"line":2626},40,[551,2628,2629],{"class":560},"    payload ",[551,2631,1484],{"class":557},[551,2633,2634],{"class":560}," text ",[551,2636,2637],{"class":557},"?",[551,2639,2530],{"class":570},[551,2641,373],{"class":560},[551,2643,2644],{"class":1082},"parse",[551,2646,2647],{"class":560},"(text) ",[551,2649,581],{"class":557},[551,2651,2186],{"class":570},[551,2653,1438],{"class":560},[551,2655,2657,2660,2663],{"class":553,"line":2656},41,[551,2658,2659],{"class":560},"  } ",[551,2661,2662],{"class":557},"catch",[551,2664,2623],{"class":560},[551,2666,2668,2670,2672,2674],{"class":553,"line":2667},42,[551,2669,2629],{"class":560},[551,2671,1484],{"class":557},[551,2673,2186],{"class":570},[551,2675,1438],{"class":560},[551,2677,2679],{"class":553,"line":2678},43,[551,2680,2265],{"class":560},[551,2682,2684,2686,2689,2692],{"class":553,"line":2683},44,[551,2685,2234],{"class":557},[551,2687,2688],{"class":560}," (",[551,2690,2691],{"class":557},"!",[551,2693,2694],{"class":560},"res.ok) {\n",[551,2696,2698,2701,2704,2706,2709,2712,2714,2717,2720,2722,2725,2728,2730,2733,2736,2738,2740,2743,2745,2747,2749,2751,2754,2756],{"class":553,"line":2697},45,[551,2699,2700],{"class":557},"    const",[551,2702,2703],{"class":570}," err",[551,2705,1079],{"class":557},[551,2707,2708],{"class":557}," new",[551,2710,2711],{"class":1082}," Error",[551,2713,1086],{"class":560},[551,2715,2716],{"class":584},"`${",[551,2718,2719],{"class":560},"res",[551,2721,373],{"class":584},[551,2723,2724],{"class":560},"status",[551,2726,2727],{"class":584},"} ${",[551,2729,1841],{"class":560},[551,2731,2732],{"class":584},"?.",[551,2734,2735],{"class":560},"error",[551,2737,2732],{"class":584},[551,2739,361],{"class":560},[551,2741,2742],{"class":584},"}: ${",[551,2744,1841],{"class":560},[551,2746,2732],{"class":584},[551,2748,2735],{"class":560},[551,2750,2732],{"class":584},[551,2752,2753],{"class":560},"message",[551,2755,2441],{"class":584},[551,2757,1092],{"class":560},[551,2759,2761,2764,2766],{"class":553,"line":2760},46,[551,2762,2763],{"class":560},"    err.status ",[551,2765,1484],{"class":557},[551,2767,2768],{"class":560}," res.status;\n",[551,2770,2772,2775,2777],{"class":553,"line":2771},47,[551,2773,2774],{"class":560},"    err.code ",[551,2776,1484],{"class":557},[551,2778,2779],{"class":560}," payload?.error?.code;\n",[551,2781,2783,2786,2788,2791,2794],{"class":553,"line":2782},48,[551,2784,2785],{"class":560},"    err.requestId ",[551,2787,1484],{"class":557},[551,2789,2790],{"class":560}," payload?.error?.request_id ",[551,2792,2793],{"class":557},"??",[551,2795,2796],{"class":560}," requestId;\n",[551,2798,2800,2803],{"class":553,"line":2799},49,[551,2801,2802],{"class":557},"    throw",[551,2804,2805],{"class":560}," err;\n",[551,2807,2809],{"class":553,"line":2808},50,[551,2810,2265],{"class":560},[551,2812,2814,2816],{"class":553,"line":2813},51,[551,2815,1218],{"class":557},[551,2817,2818],{"class":560}," payload;\n",[551,2820,2822],{"class":553,"line":2821},52,[551,2823,708],{"class":560},[551,2825,2827],{"class":553,"line":2826},53,[551,2828,1098],{"emptyLinePlaceholder":1097},[551,2830,2832,2834,2836,2838,2840],{"class":553,"line":2831},54,[551,2833,1313],{"class":570},[551,2835,373],{"class":560},[551,2837,1318],{"class":570},[551,2839,1079],{"class":557},[551,2841,2842],{"class":560}," { getToken, api };\n",[650,2844,311],{"id":1406},[357,2846,2847],{},"A PHP-FPM worker does not keep variables between requests, so an in-memory cache only helps inside one long-running script such as a queue worker or a cron job. For web requests you have two good options: sign a fresh token per request, or keep the token in a shared cache such as APCu. The class below does the second when APCu is available and falls back to the first.",[542,2849,2851],{"className":1404,"code":2850,"language":1406,"meta":547,"style":547},"\u003C?php\nuse Firebase\\JWT\\JWT;\n\nfinal class MtoToken\n{\n    private const LIFETIME = 3600;\n    private const MARGIN = 60;\n    private const CACHE_KEY = 'mto_api_token';\n\n    private ?string $token = null;\n    private int $exp = 0;\n\n    public function __construct(\n        private readonly string $apiKey,\n        private readonly string $apiSecret,\n    ) {}\n\n    public function get(): string\n    {\n        $now = time();\n\n        if ($this->token !== null && $this->exp - self::MARGIN > $now) {\n            return $this->token;\n        }\n\n        if (function_exists('apcu_fetch')) {\n            $cached = apcu_fetch(self::CACHE_KEY);\n            if (is_array($cached) && $cached['exp'] - self::MARGIN > $now) {\n                [$this->token, $this->exp] = [$cached['token'], $cached['exp']];\n                return $this->token;\n            }\n        }\n\n        $iat = $now;\n        $this->exp = $iat + self::LIFETIME;\n        $this->token = JWT::encode(\n            ['sub' => $this->apiKey, 'iat' => $iat, 'exp' => $this->exp],\n            $this->apiSecret,\n            'HS256',\n            $this->apiKey\n        );\n\n        if (function_exists('apcu_store')) {\n            \u002F\u002F Only the token is cached, never the secret.\n            apcu_store(self::CACHE_KEY, ['token' => $this->token, 'exp' => $this->exp],\n                $this->exp - $now - self::MARGIN);\n        }\n\n        return $this->token;\n    }\n}\n\n$tokens = new MtoToken(getenv('MTO_API_KEY'), getenv('MTO_API_SECRET'));\n$ch = curl_init('https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Fvalidate-me');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $tokens->get()],\n]);\n$account = json_decode(curl_exec($ch), true);\n",[361,2852,2853,2859,2867,2871,2882,2886,2903,2918,2934,2938,2954,2971,2975,2987,3001,3012,3017,3021,3036,3040,3051,3055,3099,3111,3116,3120,3137,3157,3190,3225,3236,3241,3245,3249,3259,3281,3299,3332,3342,3349,3358,3363,3367,3382,3387,3421,3443,3447,3451,3462,3467,3471,3475,3505,3522,3531,3544,3570,3576],{"__ignoreMap":547},[551,2854,2855,2857],{"class":553,"line":554},[551,2856,1413],{"class":557},[551,2858,1416],{"class":570},[551,2860,2861,2863,2865],{"class":553,"line":574},[551,2862,1447],{"class":557},[551,2864,1450],{"class":570},[551,2866,1438],{"class":560},[551,2868,2869],{"class":553,"line":588},[551,2870,1098],{"emptyLinePlaceholder":1097},[551,2872,2873,2876,2879],{"class":553,"line":694},[551,2874,2875],{"class":557},"final",[551,2877,2878],{"class":557}," class",[551,2880,2881],{"class":1048}," MtoToken\n",[551,2883,2884],{"class":553,"line":705},[551,2885,665],{"class":560},[551,2887,2888,2891,2894,2897,2899,2901],{"class":553,"line":1142},[551,2889,2890],{"class":557},"    private",[551,2892,2893],{"class":557}," const",[551,2895,2896],{"class":570}," LIFETIME",[551,2898,1079],{"class":557},[551,2900,1152],{"class":570},[551,2902,1438],{"class":560},[551,2904,2905,2907,2909,2912,2914,2916],{"class":553,"line":1161},[551,2906,2890],{"class":557},[551,2908,2893],{"class":557},[551,2910,2911],{"class":570}," MARGIN",[551,2913,1079],{"class":557},[551,2915,2167],{"class":570},[551,2917,1438],{"class":560},[551,2919,2920,2922,2924,2927,2929,2932],{"class":553,"line":1166},[551,2921,2890],{"class":557},[551,2923,2893],{"class":557},[551,2925,2926],{"class":570}," CACHE_KEY",[551,2928,1079],{"class":557},[551,2930,2931],{"class":584}," 'mto_api_token'",[551,2933,1438],{"class":560},[551,2935,2936],{"class":553,"line":1178},[551,2937,1098],{"emptyLinePlaceholder":1097},[551,2939,2940,2942,2945,2948,2950,2952],{"class":553,"line":1215},[551,2941,2890],{"class":557},[551,2943,2944],{"class":557}," ?string",[551,2946,2947],{"class":560}," $token ",[551,2949,1484],{"class":557},[551,2951,2186],{"class":570},[551,2953,1438],{"class":560},[551,2955,2956,2958,2961,2964,2966,2969],{"class":553,"line":1230},[551,2957,2890],{"class":557},[551,2959,2960],{"class":557}," int",[551,2962,2963],{"class":560}," $exp ",[551,2965,1484],{"class":557},[551,2967,2968],{"class":570}," 0",[551,2970,1438],{"class":560},[551,2972,2973],{"class":553,"line":1250},[551,2974,1098],{"emptyLinePlaceholder":1097},[551,2976,2977,2980,2982,2985],{"class":553,"line":1258},[551,2978,2979],{"class":557},"    public",[551,2981,2383],{"class":557},[551,2983,2984],{"class":570}," __construct",[551,2986,1227],{"class":560},[551,2988,2989,2992,2995,2998],{"class":553,"line":1264},[551,2990,2991],{"class":557},"        private",[551,2993,2994],{"class":557}," readonly",[551,2996,2997],{"class":557}," string",[551,2999,3000],{"class":560}," $apiKey,\n",[551,3002,3003,3005,3007,3009],{"class":553,"line":1275},[551,3004,2991],{"class":557},[551,3006,2994],{"class":557},[551,3008,2997],{"class":557},[551,3010,3011],{"class":560}," $apiSecret,\n",[551,3013,3014],{"class":553,"line":1288},[551,3015,3016],{"class":560},"    ) {}\n",[551,3018,3019],{"class":553,"line":1294},[551,3020,1098],{"emptyLinePlaceholder":1097},[551,3022,3023,3025,3027,3030,3032,3034],{"class":553,"line":1300},[551,3024,2979],{"class":557},[551,3026,2383],{"class":557},[551,3028,3029],{"class":1082}," get",[551,3031,2470],{"class":560},[551,3033,581],{"class":557},[551,3035,1493],{"class":557},[551,3037,3038],{"class":553,"line":1305},[551,3039,1261],{"class":560},[551,3041,3042,3045,3047,3049],{"class":553,"line":1310},[551,3043,3044],{"class":560},"        $now ",[551,3046,1484],{"class":557},[551,3048,1507],{"class":570},[551,3050,2599],{"class":560},[551,3052,3053],{"class":553,"line":2342},[551,3054,1098],{"emptyLinePlaceholder":1097},[551,3056,3057,3060,3062,3065,3068,3071,3073,3075,3078,3081,3083,3086,3088,3091,3094,3096],{"class":553,"line":2348},[551,3058,3059],{"class":557},"        if",[551,3061,2688],{"class":560},[551,3063,3064],{"class":570},"$this",[551,3066,3067],{"class":557},"->",[551,3069,3070],{"class":560},"token ",[551,3072,2486],{"class":557},[551,3074,2186],{"class":570},[551,3076,3077],{"class":557}," &&",[551,3079,3080],{"class":570}," $this",[551,3082,3067],{"class":557},[551,3084,3085],{"class":560},"exp ",[551,3087,442],{"class":557},[551,3089,3090],{"class":557}," self::",[551,3092,3093],{"class":570},"MARGIN",[551,3095,2250],{"class":557},[551,3097,3098],{"class":560}," $now) {\n",[551,3100,3101,3104,3106,3108],{"class":553,"line":2359},[551,3102,3103],{"class":557},"            return",[551,3105,3080],{"class":570},[551,3107,3067],{"class":557},[551,3109,3110],{"class":560},"token;\n",[551,3112,3113],{"class":553,"line":2367},[551,3114,3115],{"class":560},"        }\n",[551,3117,3118],{"class":553,"line":2372},[551,3119,1098],{"emptyLinePlaceholder":1097},[551,3121,3122,3124,3126,3129,3131,3134],{"class":553,"line":2377},[551,3123,3059],{"class":557},[551,3125,2688],{"class":560},[551,3127,3128],{"class":570},"function_exists",[551,3130,1086],{"class":560},[551,3132,3133],{"class":584},"'apcu_fetch'",[551,3135,3136],{"class":560},")) {\n",[551,3138,3139,3142,3144,3147,3149,3152,3155],{"class":553,"line":2418},[551,3140,3141],{"class":560},"            $cached ",[551,3143,1484],{"class":557},[551,3145,3146],{"class":1082}," apcu_fetch",[551,3148,1086],{"class":560},[551,3150,3151],{"class":557},"self::",[551,3153,3154],{"class":570},"CACHE_KEY",[551,3156,1092],{"class":560},[551,3158,3159,3162,3164,3167,3170,3172,3175,3177,3180,3182,3184,3186,3188],{"class":553,"line":2446},[551,3160,3161],{"class":557},"            if",[551,3163,2688],{"class":560},[551,3165,3166],{"class":570},"is_array",[551,3168,3169],{"class":560},"($cached) ",[551,3171,2240],{"class":557},[551,3173,3174],{"class":560}," $cached[",[551,3176,1553],{"class":584},[551,3178,3179],{"class":560},"] ",[551,3181,442],{"class":557},[551,3183,3090],{"class":557},[551,3185,3093],{"class":570},[551,3187,2250],{"class":557},[551,3189,3098],{"class":560},[551,3191,3192,3195,3197,3199,3202,3204,3206,3209,3211,3214,3217,3220,3222],{"class":553,"line":2452},[551,3193,3194],{"class":560},"                [",[551,3196,3064],{"class":570},[551,3198,3067],{"class":557},[551,3200,3201],{"class":560},"token, ",[551,3203,3064],{"class":570},[551,3205,3067],{"class":557},[551,3207,3208],{"class":560},"exp] ",[551,3210,1484],{"class":557},[551,3212,3213],{"class":560}," [$cached[",[551,3215,3216],{"class":584},"'token'",[551,3218,3219],{"class":560},"], $cached[",[551,3221,1553],{"class":584},[551,3223,3224],{"class":560},"]];\n",[551,3226,3227,3230,3232,3234],{"class":553,"line":2458},[551,3228,3229],{"class":557},"                return",[551,3231,3080],{"class":570},[551,3233,3067],{"class":557},[551,3235,3110],{"class":560},[551,3237,3238],{"class":553,"line":2477},[551,3239,3240],{"class":560},"            }\n",[551,3242,3243],{"class":553,"line":2513},[551,3244,3115],{"class":560},[551,3246,3247],{"class":553,"line":2518},[551,3248,1098],{"emptyLinePlaceholder":1097},[551,3250,3251,3254,3256],{"class":553,"line":2547},[551,3252,3253],{"class":560},"        $iat ",[551,3255,1484],{"class":557},[551,3257,3258],{"class":560}," $now;\n",[551,3260,3261,3264,3266,3268,3270,3272,3274,3276,3279],{"class":553,"line":2552},[551,3262,3263],{"class":570},"        $this",[551,3265,3067],{"class":557},[551,3267,3085],{"class":560},[551,3269,1484],{"class":557},[551,3271,1558],{"class":560},[551,3273,1242],{"class":557},[551,3275,3090],{"class":557},[551,3277,3278],{"class":570},"LIFETIME",[551,3280,1438],{"class":560},[551,3282,3283,3285,3287,3289,3291,3293,3295,3297],{"class":553,"line":2575},[551,3284,3263],{"class":570},[551,3286,3067],{"class":557},[551,3288,3070],{"class":560},[551,3290,1484],{"class":557},[551,3292,1521],{"class":570},[551,3294,1524],{"class":557},[551,3296,1527],{"class":1082},[551,3298,1227],{"class":560},[551,3300,3301,3304,3306,3308,3310,3312,3315,3317,3319,3321,3323,3325,3327,3329],{"class":553,"line":2581},[551,3302,3303],{"class":560},"            [",[551,3305,1537],{"class":584},[551,3307,1540],{"class":557},[551,3309,3080],{"class":570},[551,3311,3067],{"class":557},[551,3313,3314],{"class":560},"apiKey, ",[551,3316,1545],{"class":584},[551,3318,1540],{"class":557},[551,3320,1550],{"class":560},[551,3322,1553],{"class":584},[551,3324,1540],{"class":557},[551,3326,3080],{"class":570},[551,3328,3067],{"class":557},[551,3330,3331],{"class":560},"exp],\n",[551,3333,3334,3337,3339],{"class":553,"line":2602},[551,3335,3336],{"class":570},"            $this",[551,3338,3067],{"class":557},[551,3340,3341],{"class":560},"apiSecret,\n",[551,3343,3344,3347],{"class":553,"line":2617},[551,3345,3346],{"class":584},"            'HS256'",[551,3348,679],{"class":560},[551,3350,3351,3353,3355],{"class":553,"line":2626},[551,3352,3336],{"class":570},[551,3354,3067],{"class":557},[551,3356,3357],{"class":560},"apiKey\n",[551,3359,3360],{"class":553,"line":2656},[551,3361,3362],{"class":560},"        );\n",[551,3364,3365],{"class":553,"line":2667},[551,3366,1098],{"emptyLinePlaceholder":1097},[551,3368,3369,3371,3373,3375,3377,3380],{"class":553,"line":2678},[551,3370,3059],{"class":557},[551,3372,2688],{"class":560},[551,3374,3128],{"class":570},[551,3376,1086],{"class":560},[551,3378,3379],{"class":584},"'apcu_store'",[551,3381,3136],{"class":560},[551,3383,3384],{"class":553,"line":2683},[551,3385,3386],{"class":1067},"            \u002F\u002F Only the token is cached, never the secret.\n",[551,3388,3389,3392,3394,3396,3398,3401,3403,3405,3407,3409,3411,3413,3415,3417,3419],{"class":553,"line":2697},[551,3390,3391],{"class":1082},"            apcu_store",[551,3393,1086],{"class":560},[551,3395,3151],{"class":557},[551,3397,3154],{"class":570},[551,3399,3400],{"class":560},", [",[551,3402,3216],{"class":584},[551,3404,1540],{"class":557},[551,3406,3080],{"class":570},[551,3408,3067],{"class":557},[551,3410,3201],{"class":560},[551,3412,1553],{"class":584},[551,3414,1540],{"class":557},[551,3416,3080],{"class":570},[551,3418,3067],{"class":557},[551,3420,3331],{"class":560},[551,3422,3423,3426,3428,3430,3432,3435,3437,3439,3441],{"class":553,"line":2760},[551,3424,3425],{"class":570},"                $this",[551,3427,3067],{"class":557},[551,3429,3085],{"class":560},[551,3431,442],{"class":557},[551,3433,3434],{"class":560}," $now ",[551,3436,442],{"class":557},[551,3438,3090],{"class":557},[551,3440,3093],{"class":570},[551,3442,1092],{"class":560},[551,3444,3445],{"class":553,"line":2771},[551,3446,3115],{"class":560},[551,3448,3449],{"class":553,"line":2782},[551,3450,1098],{"emptyLinePlaceholder":1097},[551,3452,3453,3456,3458,3460],{"class":553,"line":2799},[551,3454,3455],{"class":557},"        return",[551,3457,3080],{"class":570},[551,3459,3067],{"class":557},[551,3461,3110],{"class":560},[551,3463,3464],{"class":553,"line":2808},[551,3465,3466],{"class":560},"    }\n",[551,3468,3469],{"class":553,"line":2813},[551,3470,708],{"class":560},[551,3472,3473],{"class":553,"line":2821},[551,3474,1098],{"emptyLinePlaceholder":1097},[551,3476,3477,3480,3482,3484,3487,3489,3491,3493,3495,3497,3499,3501,3503],{"class":553,"line":2826},[551,3478,3479],{"class":560},"$tokens ",[551,3481,1484],{"class":557},[551,3483,2708],{"class":557},[551,3485,3486],{"class":570}," MtoToken",[551,3488,1086],{"class":560},[551,3490,1610],{"class":570},[551,3492,1086],{"class":560},[551,3494,1615],{"class":584},[551,3496,1618],{"class":560},[551,3498,1610],{"class":570},[551,3500,1086],{"class":560},[551,3502,1625],{"class":584},[551,3504,1628],{"class":560},[551,3506,3507,3510,3512,3515,3517,3520],{"class":553,"line":2831},[551,3508,3509],{"class":560},"$ch ",[551,3511,1484],{"class":557},[551,3513,3514],{"class":570}," curl_init",[551,3516,1086],{"class":560},[551,3518,3519],{"class":584},"'https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Fvalidate-me'",[551,3521,1092],{"class":560},[551,3523,3525,3528],{"class":553,"line":3524},55,[551,3526,3527],{"class":570},"curl_setopt_array",[551,3529,3530],{"class":560},"($ch, [\n",[551,3532,3534,3537,3539,3542],{"class":553,"line":3533},56,[551,3535,3536],{"class":570},"    CURLOPT_RETURNTRANSFER",[551,3538,1540],{"class":557},[551,3540,3541],{"class":570}," true",[551,3543,679],{"class":560},[551,3545,3547,3550,3552,3555,3558,3560,3563,3565,3567],{"class":553,"line":3546},57,[551,3548,3549],{"class":570},"    CURLOPT_HTTPHEADER",[551,3551,1540],{"class":557},[551,3553,3554],{"class":560}," [",[551,3556,3557],{"class":584},"'Authorization: Bearer '",[551,3559,1432],{"class":557},[551,3561,3562],{"class":560}," $tokens",[551,3564,3067],{"class":557},[551,3566,2565],{"class":1082},[551,3568,3569],{"class":560},"()],\n",[551,3571,3573],{"class":553,"line":3572},58,[551,3574,3575],{"class":560},"]);\n",[551,3577,3579,3582,3584,3587,3589,3592,3595,3598],{"class":553,"line":3578},59,[551,3580,3581],{"class":560},"$account ",[551,3583,1484],{"class":557},[551,3585,3586],{"class":570}," json_decode",[551,3588,1086],{"class":560},[551,3590,3591],{"class":570},"curl_exec",[551,3593,3594],{"class":560},"($ch), ",[551,3596,3597],{"class":570},"true",[551,3599,1092],{"class":560},[650,3601,3603],{"id":3602},"several-servers","Several servers",[357,3605,3606,3607,3609],{},"Each of your servers or workers can sign its own tokens with the same key and secret. There is no limit on how many valid tokens an account has at one time, and nothing needs to be shared between machines. The rate limit belongs to the account, so it is shared however many servers you run (see ",[525,3608,45],{"href":46},").",[650,3611,3613],{"id":3612},"clocks","Clocks",[357,3615,3616,3617,752,3619,3621],{},"Your token's ",[361,3618,873],{},[361,3620,893],{}," come from your server's clock and are checked against ours:",[383,3623,3624,3634],{},[386,3625,3626],{},[389,3627,3628,3631],{},[392,3629,3630],{},"If your clock is",[392,3632,3633],{},"Effect",[405,3635,3636,3644,3658],{},[389,3637,3638,3641],{},[410,3639,3640],{},"Within 30 seconds of real time",[410,3642,3643],{},"Works",[389,3645,3646,3652],{},[410,3647,3648,3649],{},"More than 30 seconds ",[366,3650,3651],{},"ahead",[410,3653,3654,3655,3657],{},"Every new token is refused, because ",[361,3656,873],{}," is in the future",[389,3659,3660,3663],{},[410,3661,3662],{},"Behind",[410,3664,3665],{},"Tokens run out sooner than you expect, and a token looks expired as soon as your clock is more than an hour and 30 seconds behind",[357,3667,3668,3669,3671],{},"Run NTP on every machine that signs tokens. A ",[361,3670,372],{}," that starts suddenly on one server but not on others usually means a clock problem.",[357,3673,3674,3675,3678],{},"The ",[525,3676,3677],{"href":113},"token handling tutorial"," covers these patterns in more depth, including revoking on shutdown.",[375,3680,3682],{"id":3681},"check-a-token-validate-me","Check a token: validate-me",[357,3684,3685,3688],{},[361,3686,3687],{},"GET \u002Fv1\u002Fapi-account\u002Fvalidate-me"," returns the account the token belongs to. Use it to check a new setup, to check your integration from monitoring, and to read your current roles.",[542,3690,3692],{"className":1039,"code":3691,"language":1041,"meta":547,"style":547},"curl -s https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Fvalidate-me \\\n  -H \"Authorization: Bearer $TOKEN\"\n",[361,3693,3694,3704],{"__ignoreMap":547},[551,3695,3696,3698,3700,3702],{"class":553,"line":554},[551,3697,2003],{"class":1048},[551,3699,2006],{"class":570},[551,3701,2009],{"class":584},[551,3703,1853],{"class":557},[551,3705,3706,3708,3710,3712],{"class":553,"line":574},[551,3707,2016],{"class":570},[551,3709,2019],{"class":584},[551,3711,2022],{"class":560},[551,3713,1976],{"class":584},[542,3715,3717],{"className":656,"code":3716,"language":658,"meta":547,"style":547},"{\n  \"_id\": \"66f1a2b3c4d5e6f7a8b9c0d1\",\n  \"apiKey\": \"key_7fQx2LmN9pRtVw3YzA1bC4dE\",\n  \"companyName\": \"Northwind Learning Ltd\",\n  \"scopes\": [],\n  \"roles\": [\n    { \"effect\": \"allow\", \"action\": \"api\u002F*\", \"target\": \"mto\" },\n    { \"effect\": \"allow\", \"action\": \"*\u002Fread\", \"target\": \"*\" },\n    { \"effect\": \"allow\", \"action\": \"student\u002F*\", \"target\": \"mto\" }\n  ],\n  \"isActive\": true\n}\n",[361,3718,3719,3723,3735,3746,3758,3766,3774,3809,3839,3869,3874,3884],{"__ignoreMap":547},[551,3720,3721],{"class":553,"line":554},[551,3722,665],{"class":560},[551,3724,3725,3728,3730,3733],{"class":553,"line":574},[551,3726,3727],{"class":577},"  \"_id\"",[551,3729,673],{"class":560},[551,3731,3732],{"class":584},"\"66f1a2b3c4d5e6f7a8b9c0d1\"",[551,3734,679],{"class":560},[551,3736,3737,3740,3742,3744],{"class":553,"line":588},[551,3738,3739],{"class":577},"  \"apiKey\"",[551,3741,673],{"class":560},[551,3743,807],{"class":584},[551,3745,679],{"class":560},[551,3747,3748,3751,3753,3756],{"class":553,"line":694},[551,3749,3750],{"class":577},"  \"companyName\"",[551,3752,673],{"class":560},[551,3754,3755],{"class":584},"\"Northwind Learning Ltd\"",[551,3757,679],{"class":560},[551,3759,3760,3763],{"class":553,"line":705},[551,3761,3762],{"class":577},"  \"scopes\"",[551,3764,3765],{"class":560},": [],\n",[551,3767,3768,3771],{"class":553,"line":1142},[551,3769,3770],{"class":577},"  \"roles\"",[551,3772,3773],{"class":560},": [\n",[551,3775,3776,3779,3782,3784,3787,3789,3792,3794,3797,3799,3802,3804,3807],{"class":553,"line":1161},[551,3777,3778],{"class":560},"    { ",[551,3780,3781],{"class":577},"\"effect\"",[551,3783,673],{"class":560},[551,3785,3786],{"class":584},"\"allow\"",[551,3788,433],{"class":560},[551,3790,3791],{"class":577},"\"action\"",[551,3793,673],{"class":560},[551,3795,3796],{"class":584},"\"api\u002F*\"",[551,3798,433],{"class":560},[551,3800,3801],{"class":577},"\"target\"",[551,3803,673],{"class":560},[551,3805,3806],{"class":584},"\"mto\"",[551,3808,1247],{"class":560},[551,3810,3811,3813,3815,3817,3819,3821,3823,3825,3828,3830,3832,3834,3837],{"class":553,"line":1166},[551,3812,3778],{"class":560},[551,3814,3781],{"class":577},[551,3816,673],{"class":560},[551,3818,3786],{"class":584},[551,3820,433],{"class":560},[551,3822,3791],{"class":577},[551,3824,673],{"class":560},[551,3826,3827],{"class":584},"\"*\u002Fread\"",[551,3829,433],{"class":560},[551,3831,3801],{"class":577},[551,3833,673],{"class":560},[551,3835,3836],{"class":584},"\"*\"",[551,3838,1247],{"class":560},[551,3840,3841,3843,3845,3847,3849,3851,3853,3855,3858,3860,3862,3864,3866],{"class":553,"line":1178},[551,3842,3778],{"class":560},[551,3844,3781],{"class":577},[551,3846,673],{"class":560},[551,3848,3786],{"class":584},[551,3850,433],{"class":560},[551,3852,3791],{"class":577},[551,3854,673],{"class":560},[551,3856,3857],{"class":584},"\"student\u002F*\"",[551,3859,433],{"class":560},[551,3861,3801],{"class":577},[551,3863,673],{"class":560},[551,3865,3806],{"class":584},[551,3867,3868],{"class":560}," }\n",[551,3870,3871],{"class":553,"line":1215},[551,3872,3873],{"class":560},"  ],\n",[551,3875,3876,3879,3881],{"class":553,"line":1230},[551,3877,3878],{"class":577},"  \"isActive\"",[551,3880,673],{"class":560},[551,3882,3883],{"class":570},"true\n",[551,3885,3886],{"class":553,"line":1250},[551,3887,708],{"class":560},[383,3889,3890,3899],{},[386,3891,3892],{},[389,3893,3894,3896],{},[392,3895,717],{},[392,3897,3898],{},"Meaning",[405,3900,3901,3918,3929,3939,3953,3968],{},[389,3902,3903,3908],{},[410,3904,3905],{},[361,3906,3907],{},"_id",[410,3909,3910,3911,3914,3915,3609],{},"Your account's id. The students you register belong to this id. A role whose ",[361,3912,3913],{},"authorized"," is set must name this id (see ",[525,3916,25],{"href":3917},"\u002Fapi\u002Fpermissions#authorized",[389,3919,3920,3924],{},[410,3921,3922],{},[361,3923,398],{},[410,3925,3926,3927,373],{},"Your key, the same as the token's ",[361,3928,463],{},[389,3930,3931,3936],{},[410,3932,3933],{},[361,3934,3935],{},"companyName",[410,3937,3938],{},"The name the operator gave your account.",[389,3940,3941,3946],{},[410,3942,3943],{},[361,3944,3945],{},"scopes",[410,3947,3948,3949,3952],{},"Informational labels set by the operator. They are not used for access decisions; ",[361,3950,3951],{},"roles"," are.",[389,3954,3955,3959],{},[410,3956,3957],{},[361,3958,3951],{},[410,3960,3961,3962,3965,3966,373],{},"Your permissions: a list of ",[361,3963,3964],{},"{ effect, action, target, authorized? }",". See ",[525,3967,25],{"href":26},[389,3969,3970,3975],{},[410,3971,3972],{},[361,3973,3974],{},"isActive",[410,3976,3977,3978,3980],{},"Always ",[361,3979,3597],{}," here, because a deactivated account cannot authenticate at all.",[357,3982,3983,3984,373],{},"The response never contains your ",[361,3985,403],{},[357,3987,3988,3991,3992,3995,3996,3999,4000,4003],{},[366,3989,3990],{},"No envelope."," Unlike every other JSON route, ",[361,3993,3994],{},"validate-me"," returns the account object on its own, not inside ",[361,3997,3998],{},"{ \"success\", \"message\", \"data\" }",". If your client unwraps ",[361,4001,4002],{},"data"," automatically, make an exception for this route.",[357,4005,4006,4009,4010,4012,4013,4016,4017,4020,4021,433,4023,4026,4027,4030,4031,4034,4035,4037,4038,4041],{},[366,4007,4008],{},"Permission."," ",[361,4011,3994],{}," needs the action ",[361,4014,4015],{},"api\u002F*"," on ",[361,4018,4019],{},"mto",". Only a role action of ",[361,4022,4015],{},[361,4024,4025],{},"*\u002F*"," or ",[361,4028,4029],{},"*"," grants it. A token that is fine but lacks this role gets ",[361,4032,4033],{},"403 forbidden",", not ",[361,4036,372],{},". That difference is useful: a ",[361,4039,4040],{},"403"," here proves your token is valid.",[357,4043,4044,4045,4048,4049,373],{},"Reference: ",[525,4046,4047],{"href":144},"getCurrentApiAccount",". More uses: ",[525,4050,4051],{"href":78},"API account guide",[375,4053,4055],{"id":4054},"revoke-a-token-revoke-token","Revoke a token: revoke-token",[357,4057,4058,4061,4062,4065,4066,4068],{},[361,4059,4060],{},"POST \u002Fv1\u002Fapi-account\u002Frevoke-token"," revokes ",[366,4063,4064],{},"the token you send it with",". From then on, that token answers ",[361,4067,372],{}," on every request. Nothing else changes: your account, your secret and your other tokens keep working, and you can sign a new token at once.",[542,4070,4072],{"className":1039,"code":4071,"language":1041,"meta":547,"style":547},"curl -s -X POST https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Frevoke-token \\\n  -H \"Authorization: Bearer $TOKEN\"\n",[361,4073,4074,4091],{"__ignoreMap":547},[551,4075,4076,4078,4080,4083,4086,4089],{"class":553,"line":554},[551,4077,2003],{"class":1048},[551,4079,2006],{"class":570},[551,4081,4082],{"class":570}," -X",[551,4084,4085],{"class":584}," POST",[551,4087,4088],{"class":584}," https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fapi-account\u002Frevoke-token",[551,4090,1853],{"class":557},[551,4092,4093,4095,4097,4099],{"class":553,"line":574},[551,4094,2016],{"class":570},[551,4096,2019],{"class":584},[551,4098,2022],{"class":560},[551,4100,1976],{"class":584},[542,4102,4104],{"className":656,"code":4103,"language":658,"meta":547,"style":547},"{\n  \"success\": true,\n  \"message\": \"Token revoked successfully\",\n  \"data\": { \"expiresIn\": 3412 }\n}\n",[361,4105,4106,4110,4121,4133,4151],{"__ignoreMap":547},[551,4107,4108],{"class":553,"line":554},[551,4109,665],{"class":560},[551,4111,4112,4115,4117,4119],{"class":553,"line":574},[551,4113,4114],{"class":577},"  \"success\"",[551,4116,673],{"class":560},[551,4118,3597],{"class":570},[551,4120,679],{"class":560},[551,4122,4123,4126,4128,4131],{"class":553,"line":588},[551,4124,4125],{"class":577},"  \"message\"",[551,4127,673],{"class":560},[551,4129,4130],{"class":584},"\"Token revoked successfully\"",[551,4132,679],{"class":560},[551,4134,4135,4138,4141,4144,4146,4149],{"class":553,"line":694},[551,4136,4137],{"class":577},"  \"data\"",[551,4139,4140],{"class":560},": { ",[551,4142,4143],{"class":577},"\"expiresIn\"",[551,4145,673],{"class":560},[551,4147,4148],{"class":570},"3412",[551,4150,3868],{"class":560},[551,4152,4153],{"class":553,"line":705},[551,4154,708],{"class":560},[357,4156,4157,4159],{},[361,4158,1344],{}," is how many seconds the revocation is kept: the token's remaining lifetime plus the 30 seconds of clock tolerance, at least 1 and at most 3660. After that, the token is refused because it has expired, so no revocation needs to be kept.",[357,4161,4162,4163,4165],{},"The request has no body. The only token it can revoke is the one in its own ",[361,4164,539],{}," header, so to revoke a token you must still hold it.",[383,4167,4168,4178],{},[386,4169,4170],{},[389,4171,4172,4175],{},[392,4173,4174],{},"Answer",[392,4176,4177],{},"When",[405,4179,4180,4190,4200,4216],{},[389,4181,4182,4187],{},[410,4183,4184],{},[361,4185,4186],{},"200",[410,4188,4189],{},"The token is revoked",[389,4191,4192,4197],{},[410,4193,4194],{},[361,4195,4196],{},"401 unauthorized",[410,4198,4199],{},"The token was already invalid: expired, revoked, or otherwise refused. There is nothing left to revoke",[389,4201,4202,4206],{},[410,4203,4204],{},[361,4205,4033],{},[410,4207,4208,4209,4016,4211,4213,4214],{},"Your account lacks ",[361,4210,4015],{},[361,4212,4019],{},", the permission this route needs, like ",[361,4215,3994],{},[389,4217,4218,4223],{},[410,4219,4220],{},[361,4221,4222],{},"400 bad_request",[410,4224,4225,4026,4228,4231,4232,4234],{},[361,4226,4227],{},"No token provided.",[361,4229,4230],{},"Invalid token format.",": the token could not be read or carries no ",[361,4233,893],{},". A token that passed authentication always has one, so you should not see this",[357,4236,4237],{},"When to revoke:",[1328,4239,4240,4246,4252],{},[603,4241,4242,4245],{},[366,4243,4244],{},"On shutdown"," of a long-running worker, so that a token left in memory or in a crash dump is worthless.",[603,4247,4248,4251],{},[366,4249,4250],{},"When a token may have leaked",", for example if it was written to a log or pasted into a ticket.",[603,4253,4254,4257],{},[366,4255,4256],{},"When you rotate"," to a new token early for any reason.",[515,4259,4261],{"type":4260},"note",[357,4262,4263,4264,4267,4268,4271,4272,4274],{},"HS256 signing is deterministic. Two tokens with the same header and the same claims are the same string, so if two of your workers sign ",[361,4265,4266],{},"{ sub, iat, exp }"," in the same second, they hold one token, and revoking it cuts off both. If your workers revoke on shutdown, give each token something unique, for example a random ",[361,4269,4270],{},"jti"," claim. The API ignores ",[361,4273,4270],{},", but it makes every token distinct.",[357,4276,4277,4278,4281,4282,752,4286,373],{},"Revoking a token does not protect you from a leaked ",[366,4279,4280],{},"secret",": whoever holds the secret can sign new tokens. For that, see ",[525,4283,4285],{"href":4284},"#changes-to-your-account","Changes to your account",[525,4287,528],{"href":527},[357,4289,4044,4290,373],{},[525,4291,4292],{"href":140},"revokeToken",[375,4294,4285],{"id":4295},"changes-to-your-account",[357,4297,4298],{},"Operators manage accounts; there is no route to change your own. What you can expect:",[383,4300,4301,4314],{},[386,4302,4303],{},[389,4304,4305,4308,4311],{},[392,4306,4307],{},"Change",[392,4309,4310],{},"Who makes it",[392,4312,4313],{},"When it takes effect",[405,4315,4316,4327,4339,4353],{},[389,4317,4318,4321,4324],{},[410,4319,4320],{},"Your roles are changed",[410,4322,4323],{},"Operator",[410,4325,4326],{},"Within 60 seconds. Roles belong to your account, not to the token, so tokens you already hold pick up the new roles; you do not need to sign new ones",[389,4328,4329,4332,4334],{},[410,4330,4331],{},"Your account is deactivated",[410,4333,4323],{},[410,4335,4336,4337],{},"Within 60 seconds, every token of the account answers ",[361,4338,372],{},[389,4340,4341,4344,4350],{},[410,4342,4343],{},"A single token is revoked",[410,4345,4346,4347],{},"You, with ",[361,4348,4349],{},"revoke-token",[410,4351,4352],{},"At once",[389,4354,4355,4358,4361],{},[410,4356,4357],{},"Your secret is changed",[410,4359,4360],{},"Not possible",[410,4362,4363],{},"A new secret means a new account",[375,4365,528],{"id":4366},"if-your-secret-leaks",[600,4368,4369,4382,4388,4398],{},[603,4370,4371,4378,4379,4381],{},[366,4372,4373,4374],{},"Email ",[525,4375,4377],{"href":4376},"mailto:info@main-team.org","info@main-team.org"," from the address you normally use with us. Ask for the account to be deactivated, and give its ",[361,4380,398],{}," (never the secret). Within 60 seconds of deactivation, every token signed with the secret stops working.",[603,4383,4384,4387],{},[366,4385,4386],{},"Revoke the tokens you hold",", if you want to cut them off before the operator acts. This does not stop new tokens being signed with the leaked secret; only deactivation does.",[603,4389,4390,4393,4394,4397],{},[366,4391,4392],{},"Find the cause and fix it"," (a committed ",[361,4395,4396],{},".env"," file, a log line, a shared screen) before you get new credentials.",[603,4399,4400,4403],{},[366,4401,4402],{},"Agree the replacement with the operator."," A new account comes with a new key and secret. Students belong to the account that registered them, so a new account does not see the students the old account registered. Discuss with the operator how to handle them before you switch.",[375,4405,638],{"id":4406},"the-401-checklist",[357,4408,4409],{},"Every authentication failure answers exactly this:",[542,4411,4413],{"className":656,"code":4412,"language":658,"meta":547,"style":547},"{\n  \"error\": {\n    \"code\": \"unauthorized\",\n    \"message\": \"Authentication is required or the provided credentials are invalid.\",\n    \"documentation_url\": \"https:\u002F\u002Fhub.main-team.org\u002Fapi\u002Ferrors#unauthorized\",\n    \"request_id\": \"0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e\"\n  }\n}\n",[361,4414,4415,4419,4427,4439,4451,4463,4473,4477],{"__ignoreMap":547},[551,4416,4417],{"class":553,"line":554},[551,4418,665],{"class":560},[551,4420,4421,4424],{"class":553,"line":574},[551,4422,4423],{"class":577},"  \"error\"",[551,4425,4426],{"class":560},": {\n",[551,4428,4429,4432,4434,4437],{"class":553,"line":588},[551,4430,4431],{"class":577},"    \"code\"",[551,4433,673],{"class":560},[551,4435,4436],{"class":584},"\"unauthorized\"",[551,4438,679],{"class":560},[551,4440,4441,4444,4446,4449],{"class":553,"line":694},[551,4442,4443],{"class":577},"    \"message\"",[551,4445,673],{"class":560},[551,4447,4448],{"class":584},"\"Authentication is required or the provided credentials are invalid.\"",[551,4450,679],{"class":560},[551,4452,4453,4456,4458,4461],{"class":553,"line":705},[551,4454,4455],{"class":577},"    \"documentation_url\"",[551,4457,673],{"class":560},[551,4459,4460],{"class":584},"\"https:\u002F\u002Fhub.main-team.org\u002Fapi\u002Ferrors#unauthorized\"",[551,4462,679],{"class":560},[551,4464,4465,4468,4470],{"class":553,"line":1142},[551,4466,4467],{"class":577},"    \"request_id\"",[551,4469,673],{"class":560},[551,4471,4472],{"class":584},"\"0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e\"\n",[551,4474,4475],{"class":553,"line":1161},[551,4476,2265],{"class":560},[551,4478,4479],{"class":553,"line":1166},[551,4480,708],{"class":560},[357,4482,4483,4484,4487,4488,4490],{},"The API never says which check failed. A distinct message would tell someone holding a stolen key whether it is live, or a stolen token whether it was revoked. Our logs do record the reason against the ",[361,4485,4486],{},"request_id",", so if you are stuck after this checklist, send us the ",[361,4489,4486],{}," (never the token).",[357,4492,4493],{},"Go through the list in order. It follows the order in which the API checks.",[383,4495,4496,4509],{},[386,4497,4498],{},[389,4499,4500,4503,4506],{},[392,4501,4502],{},"#",[392,4504,4505],{},"Check",[392,4507,4508],{},"Typical mistake",[405,4510,4511,4528,4539,4561,4572,4587,4603,4623,4640,4661,4674,4693,4709],{},[389,4512,4513,4516,4522],{},[410,4514,4515],{},"1",[410,4517,4518,4519],{},"The header is ",[361,4520,4521],{},"Authorization: Bearer \u003Ctoken>",[410,4523,4524,4527],{},[361,4525,4526],{},"bearer"," in lower case, two spaces, quotes around the token, a missing header after a proxy or redirect",[389,4529,4530,4533,4536],{},[410,4531,4532],{},"2",[410,4534,4535],{},"The token is three base64url parts separated by dots, and its header and payload are JSON",[410,4537,4538],{},"A line break or whitespace inside the token; the token URL-encoded twice",[389,4540,4541,4544,4556],{},[410,4542,4543],{},"3",[410,4545,4546,4547,4549,4550,4552,4553,4555],{},"The header has ",[361,4548,463],{},", and it equals your ",[361,4551,398],{}," exactly: ",[361,4554,428],{}," plus 24 characters",[410,4557,4558,4560],{},[361,4559,463],{}," in the payload instead of the header; the key trimmed or with a trailing newline; a key from another environment",[389,4562,4563,4566,4569],{},[410,4564,4565],{},"4",[410,4567,4568],{},"Your account is active",[410,4570,4571],{},"The account was deactivated",[389,4573,4574,4577,4584],{},[410,4575,4576],{},"5",[410,4578,4579,4581,4582],{},[361,4580,732],{}," is ",[361,4583,741],{},[410,4585,4586],{},"The library defaulted to another algorithm",[389,4588,4589,4592,4598],{},[410,4590,4591],{},"6",[410,4593,4594,4595,4597],{},"The signature uses your full ",[361,4596,403],{}," as text",[410,4599,3674,4600,4602],{},[361,4601,451],{}," prefix dropped; a trailing newline from a file; the secret base64-decoded first; a secret from another account",[389,4604,4605,4608,4617],{},[410,4606,4607],{},"7",[410,4609,4610,4611,4613,4614,4616],{},"The token has not expired (30 seconds of grace after ",[361,4612,893],{},"), and any ",[361,4615,912],{}," has passed",[410,4618,4619,4620,4622],{},"A cached token used past its ",[361,4621,893],{},"; the clock runs slow",[389,4624,4625,4628,4635],{},[410,4626,4627],{},"8",[410,4629,4630,752,4632,4634],{},[361,4631,873],{},[361,4633,893],{}," are both present, as numbers",[410,4636,4637,4639],{},[361,4638,1363],{}," set; claims sent as strings",[389,4641,4642,4645,4655],{},[410,4643,4644],{},"9",[410,4646,4647,752,4649,4651,4652],{},[361,4648,873],{},[361,4650,893],{}," are in ",[366,4653,4654],{},"seconds",[410,4656,4657,4660],{},[361,4658,4659],{},"Date.now()"," (milliseconds) used directly",[389,4662,4663,4666,4671],{},[410,4664,4665],{},"10",[410,4667,4668,4670],{},[361,4669,873],{}," is not more than 30 seconds ahead of real time",[410,4672,4673],{},"The server clock runs fast; no NTP",[389,4675,4676,4679,4687],{},[410,4677,4678],{},"11",[410,4680,4681,4683,4684,4686],{},[361,4682,893],{}," is later than ",[361,4685,873],{},", by at most 3600 seconds",[410,4688,4689,4690],{},"A 24-hour token; ",[361,4691,4692],{},"expiresIn: '2h'",[389,4694,4695,4698,4704],{},[410,4696,4697],{},"12",[410,4699,4700,620,4702],{},[361,4701,467],{},[361,4703,463],{},[410,4705,4706,4708],{},[361,4707,467],{}," set to a user id or your company name",[389,4710,4711,4714,4717],{},[410,4712,4713],{},"13",[410,4715,4716],{},"The token has not been revoked",[410,4718,4719],{},"The token was revoked at shutdown and then reused",[650,4721,4723],{"id":4722},"decode-your-token-locally","Decode your token locally",[357,4725,4726],{},"Inspect your token on your own machine. Never paste it into a website.",[542,4728,4730],{"className":1039,"code":4729,"language":1041,"meta":547,"style":547},"node -e '\nconst [h, p] = process.argv[1].split(\".\");\nconsole.log(JSON.parse(Buffer.from(h, \"base64url\")));\nconst claims = JSON.parse(Buffer.from(p, \"base64url\"));\nconst now = Math.floor(Date.now() \u002F 1000);\nconsole.log(claims);\nconsole.log({ lifetime: claims.exp - claims.iat, iatVsNow: claims.iat - now, expiresIn: claims.exp - now });\n' \"$TOKEN\"\n",[361,4731,4732,4742,4747,4752,4757,4762,4767,4772],{"__ignoreMap":547},[551,4733,4734,4737,4739],{"class":553,"line":554},[551,4735,4736],{"class":1048},"node",[551,4738,1756],{"class":570},[551,4740,4741],{"class":584}," '\n",[551,4743,4744],{"class":553,"line":574},[551,4745,4746],{"class":584},"const [h, p] = process.argv[1].split(\".\");\n",[551,4748,4749],{"class":553,"line":588},[551,4750,4751],{"class":584},"console.log(JSON.parse(Buffer.from(h, \"base64url\")));\n",[551,4753,4754],{"class":553,"line":694},[551,4755,4756],{"class":584},"const claims = JSON.parse(Buffer.from(p, \"base64url\"));\n",[551,4758,4759],{"class":553,"line":705},[551,4760,4761],{"class":584},"const now = Math.floor(Date.now() \u002F 1000);\n",[551,4763,4764],{"class":553,"line":1142},[551,4765,4766],{"class":584},"console.log(claims);\n",[551,4768,4769],{"class":553,"line":1161},[551,4770,4771],{"class":584},"console.log({ lifetime: claims.exp - claims.iat, iatVsNow: claims.iat - now, expiresIn: claims.exp - now });\n",[551,4773,4774,4777,4779,4781],{"class":553,"line":1166},[551,4775,4776],{"class":584},"'",[551,4778,1823],{"class":584},[551,4780,2022],{"class":560},[551,4782,1976],{"class":584},[542,4784,4788],{"className":4785,"code":4787,"language":2596,"meta":547},[4786],"language-text","{ alg: 'HS256', typ: 'JWT', kid: 'key_7fQx2LmN9pRtVw3YzA1bC4dE' }\n{ sub: 'key_7fQx2LmN9pRtVw3YzA1bC4dE', iat: 1789481600, exp: 1789485200 }\n{ lifetime: 3600, iatVsNow: -12, expiresIn: 3588 }\n",[361,4789,4787],{"__ignoreMap":547},[357,4791,4792,4793,433,4796,752,4798,4800,4801,4803,4804,4807,4808,4810],{},"A good token shows ",[361,4794,4795],{},"alg: 'HS256'",[361,4797,463],{},[361,4799,467],{}," both equal to your key, ",[361,4802,1719],{}," of 3600 or less, and ",[361,4805,4806],{},"iatVsNow"," of 30 or less. If all of that is right and you still get ",[361,4809,372],{},", the signature or the secret is the problem (check 6).",[650,4812,4814],{"id":4813},"is-it-401-or-403","Is it 401 or 403?",[383,4816,4817,4829],{},[386,4818,4819],{},[389,4820,4821,4824,4826],{},[392,4822,4823],{},"Status",[392,4825,3898],{},[392,4827,4828],{},"What helps",[405,4830,4831,4843],{},[389,4832,4833,4837,4840],{},[410,4834,4835],{},[361,4836,4196],{},[410,4838,4839],{},"The API does not accept the token",[410,4841,4842],{},"Fix the token, using the checklist above",[389,4844,4845,4849,4852],{},[410,4846,4847],{},[361,4848,4033],{},[410,4850,4851],{},"The token is fine, but your account has no role for this route",[410,4853,4854,4855],{},"Ask the operator for the role. A new token does not help. See ",[525,4856,25],{"href":26},[357,4858,4859,4860,4862,4863,4866],{},"A request refused with ",[361,4861,372],{},", or with ",[361,4864,4865],{},"403 Insufficient role permissions",", does not count against your rate limit.",[375,4868,4870],{"id":4869},"what-not-to-do","What not to do",[1328,4872,4873,4882,4888,4897],{},[603,4874,4875,4878,4879,373],{},[366,4876,4877],{},"Do not call the API from a browser or an app."," The API sends no CORS headers, and the secret would be exposed. See ",[525,4880,16],{"href":4881},"\u002Fapi\u002Fenvironments#call-the-api-from-your-servers",[603,4883,4884,4887],{},[366,4885,4886],{},"Do not share one token across environments or accounts."," Each token is tied to one key.",[603,4889,4890,4893,4894,4896],{},[366,4891,4892],{},"Do not log tokens."," Redact the ",[361,4895,539],{}," header in your HTTP logs. A token is a credential for up to an hour.",[603,4898,4899,4902],{},[366,4900,4901],{},"Do not make tokens longer-lived than you need."," One hour is the maximum; shorter is fine.",[357,4904,4905,4906,373],{},"More on protecting your integration: ",[525,4907,51],{"href":52},[4909,4910,4911],"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 .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 .s-5SL, html code.shiki .s-5SL{--shiki-default:#023B95;--shiki-dark:#91CBFF}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}",{"title":547,"searchDepth":574,"depth":588,"links":4913},[4914,4915,4916,4922,4928,4934,4935,4936,4937,4938,4942],{"id":377,"depth":574,"text":378},{"id":532,"depth":574,"text":533},{"id":641,"depth":574,"text":642,"children":4917},[4918,4919,4920,4921],{"id":652,"depth":588,"text":653},{"id":787,"depth":588,"text":788},{"id":923,"depth":588,"text":924},{"id":947,"depth":588,"text":948},{"id":1028,"depth":574,"text":1029,"children":4923},[4924,4925,4926,4927],{"id":1035,"depth":588,"text":1036},{"id":1385,"depth":588,"text":1386},{"id":1643,"depth":588,"text":1644},{"id":2036,"depth":588,"text":2037},{"id":2061,"depth":574,"text":2062,"children":4929},[4930,4931,4932,4933],{"id":2085,"depth":588,"text":308},{"id":1406,"depth":588,"text":311},{"id":3602,"depth":588,"text":3603},{"id":3612,"depth":588,"text":3613},{"id":3681,"depth":574,"text":3682},{"id":4054,"depth":574,"text":4055},{"id":4295,"depth":574,"text":4285},{"id":4366,"depth":574,"text":528},{"id":4406,"depth":574,"text":638,"children":4939},[4940,4941],{"id":4722,"depth":588,"text":4723},{"id":4813,"depth":588,"text":4814},{"id":4869,"depth":574,"text":4870},"How to sign HS256 tokens with your apiSecret, cache and revoke them, and fix any 401. Includes working Node.js, PHP and bash code.","md",{},[4047,4292],"30",{"title":19,"description":4943},"api\u002Fauthentication","M0ZGVOmuNA2nqneO1Vtz408fbcA3ASwgg2VVJUYm9ho",[4952,4957],{"operationId":4047,"slug":4953,"method":133,"path":4954,"tag":77,"summary":143,"deprecated":134,"public":134,"permission":4955,"scope":4956,"order":588},"get-current-api-account","\u002Fv1\u002Fapi-account\u002Fvalidate-me","api\u002F*:$org:$ID","flat",{"operationId":4292,"slug":4349,"method":141,"path":4958,"tag":77,"summary":139,"deprecated":134,"public":134,"permission":4955,"scope":4956,"order":574},"\u002Fv1\u002Fapi-account\u002Frevoke-token",1791554614899]