Exchange an authorization code for a grant
Turns the one-time code the pilot’s consent produced into the grant, for the partner the code was issued to. The code is single use and valid 10 minutes from the consent; code_verifier must be the PKCE verifier whose S256 challenge the partner sent (RFC 7636), and redirect_uri the exact URI the consent redirected to. Every refusal is 400 invalid_request with reason invalid_grant, whatever was wrong (unknown, expired or used code, another partner’s code, a wrong verifier or redirect URI, a grant the pilot revoked since): restart the consent. Of two exchanges of one code, exactly one succeeds. The answer names the grant, the pilot’s pairwise id (stable across reconnects, different at every partner, never the MPI account id) and the scopes the pilot granted; with the profile scope it adds his name, username and avatar URL (each null when he has none). Exchanges take no Idempotency-Key: a retried exchange after a lost answer gets invalid_grant, as in OAuth.
Authorizations
OAuth 2.0 client credentials. Request a token from the token endpoint of the identity provider's domain (https://{cognito-domain}/oauth2/token, with HTTP Basic client authentication) and send it as a Bearer token. Production uses auth.mypilotindex.com. The sandbox uses the dev environment's Cognito domain, which is given to the partner with its sandbox credential. A token carries only the scopes the partner was approved for.
Body
Maximum 4096 bytes. Exactly code, code_verifier and redirect_uri, every one a string; unknown or repeated members are rejected.
Response
The grant the code opened.
The grant every later call names.
The pilot. id is his pairwise id at this partner. name, username and avatar_url are present only with the profile scope, each null when he has none.
The scopes the pilot granted, sorted.
A scope the pilot granted, without the partner/ prefix of the token's scopes.
profile, score, hours.read, hours.write, licenses, applications 
