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:
#!/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#).
-
get-token.mjs
Node: gets a token and keeps it in memory until shortly before it runs out.
-
get_token.py
Python: gets a token and keeps it in memory, with the standard library only.
-
get-token.php
PHP: gets a token and keeps it in a small private file until shortly before it runs out.
-
dotnet/Program.cs
C# (.NET 8) with three modes, get-token, send-lead and token-server. Copy the SubcontextTokens class into your own code.
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, withinvalid_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).