Get a token

Ask Subcontext for an access token with your application's client id and secret, and keep reusing it until shortly before it runs out.

Ask for a token#

Your server sends a POST to https://subcontext.com/oauth/token, with the values below as a form (Content-Type: application/x-www-form-urlencoded).

Value What to send
grant_type client_credentials
client_id Your application's Client ID
client_secret Your application's Client secret
resource https://subcontext.com/api/v1, exactly. It says what the token is for. Required.
scope Optional. The scopes you want, separated by spaces, like read write. Leave it out to get every scope ticked on the application. Asking for a scope that isn't ticked on the application is refused with invalid_scope.

You can also send the client id and secret with HTTP Basic authentication instead of in the form. A JSON body also works.

A good answer (200 OK):

Field What it is
access_token The token. Send it as Authorization: Bearer <token> on each API request.
token_type Always Bearer.
expires_in How many seconds it lasts: 7200 (two hours).
scope The scopes it carries: the ones you asked for, or, if you didn't ask, all of the application's.
created_at When it was made, in seconds since 1970.

Here is the whole request with curl, from get-token.sh:

get-token.sh ViewDownload
#!/usr/bin/env sh
# Asks Subcontext for an access token and prints the answer.
#
# An access token lets your code use the Subcontext API for two hours.
# You get one by sending your API application's client id
# and client secret to Subcontext (an OAuth "client credentials" request).
#
# Run it on your computer or your server, never in a web page:
#
#   SUBCONTEXT_CLIENT_ID=... SUBCONTEXT_CLIENT_SECRET=... sh get-token.sh
#
# SUBCONTEXT_CLIENT_ID and SUBCONTEXT_CLIENT_SECRET: from the Subcontext
#   dashboard, Integrations, API applications. The client secret is shown only
#   once, when you create the application (or rotate its secret).
# SUBCONTEXT_SCOPE (optional): what the token may do, separated by spaces.
#   Default "read". It can only include scopes ticked on the API application,
#   for example "read write crm".
# SUBCONTEXT_BASE_URL (optional): the Subcontext address. Default
#   https://subcontext.com.
#
# Needs curl. A good answer looks like:
#   {"access_token":"...","token_type":"Bearer","expires_in":7200,"scope":"read","created_at":...}

base_url="${SUBCONTEXT_BASE_URL:-https://subcontext.com}"
base_url="${base_url%/}"
scope="${SUBCONTEXT_SCOPE:-read}"

if [ -z "$SUBCONTEXT_CLIENT_ID" ] || [ -z "$SUBCONTEXT_CLIENT_SECRET" ]; then
  echo "Set SUBCONTEXT_CLIENT_ID and SUBCONTEXT_CLIENT_SECRET first (see the comment at the top of this file)." >&2
  exit 1
fi

# --data-urlencode keeps any unusual characters in the secret safe.
# resource says what the token is for (the Subcontext API). It is required.
curl --silent --show-error \
  --request POST "$base_url/oauth/token" \
  --header "Accept: application/json" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=$SUBCONTEXT_CLIENT_ID" \
  --data-urlencode "client_secret=$SUBCONTEXT_CLIENT_SECRET" \
  --data-urlencode "scope=$scope" \
  --data-urlencode "resource=$base_url/api/v1" \
  --write-out "\n(HTTP status %{http_code})\n"

The same in other languages: get-token.mjs (Node), get_token.py (Python), get-token.php (PHP) and dotnet get-token in Program.cs (C#).

How long a token lasts#

  • A token lasts two hours. There is no refresh token. When it runs out, ask for a new one the same way.
  • Keep the token in memory and use it for every request until shortly before it runs out (five minutes before is a good margin), then ask again. Don't ask for a new token for every request. Token requests count towards the rate limit, and so does everything else from the same server.
  • If the API answers 401 Unauthorized, forget the token, get a new one and try the request once more. That covers a token that stopped early, for example because someone rotated the secret, removed a scope or deleted the application. (After a rotation the new token request fails too, with invalid_client, until your server has the new secret.)
  • One token can be shared by all the work on a server. get-token.mjs, get_token.py and the .NET example show an in-memory cache you can copy. PHP usually starts afresh for every web request, so get-token.php keeps the token in a small private file instead (or use your framework's cache).

Stuck, or something missing?

Get in touch and a person on the team will help.

Contact us