Open Api > Third-party application API access
Use the client credentials flow
Client credentials authentication for server apps
Section titled “Client credentials authentication for server apps”The OAuth 2.0 client credentials flow handles machine-to-machine authentication. It’s designed for backend services and third-party apps that interact with the Tallyfy API without a user logging in.
You’ll need a Client ID and Client Secret from Tallyfy Support before you start.
How the flow works
Section titled “How the flow works”What to notice
Section titled “What to notice”- Step 1 is manual - contacting Tallyfy Support for credentials is a one-time setup you can’t automate
- Two token types - application tokens (system operations) and user-specific tokens (acting as a particular user)
- Different lifetimes - application tokens expire in 7 days (604,800 seconds), user-specific tokens expire in 3 months (7,776,000 seconds)
When to use this
Section titled “When to use this”This pattern works well when you want to:
- Embed Tallyfy functionality inside your own software
- Automate process management or user provisioning
- Build system-level integrations (reporting, data sync)
- Provide workflow features to your users without separate Tallyfy logins
1. Request client credentials
Section titled “1. Request client credentials”Contact Tallyfy Support with your integration use case. They’ll provide a Client ID and Client Secret for your organization. Store these securely.
2. Get an application access token
Section titled “2. Get an application access token”Your app first needs its own access token for operations like user provisioning or generating user-specific tokens.
- Endpoint:
POST https://go.tallyfy.com/oauth/token - Content-Type:
application/x-www-form-urlencoded - Parameters:
grant_type:client_credentialsclient_id: Your Client IDclient_secret: Your Client Secretscope:*(optional)
const clientId = 'YOUR_CLIENT_ID';const clientSecret = 'YOUR_CLIENT_SECRET';const tokenUrl = 'https://go.tallyfy.com/oauth/token';
const params = new URLSearchParams();params.append('grant_type', 'client_credentials');params.append('client_id', clientId);params.append('client_secret', clientSecret);params.append('scope', '*');
const response = await fetch(tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params});
if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`);}
const data = await response.json();console.log('Access token:', data.access_token);// Use data.access_token for subsequent API callsimport requests
client_id = 'YOUR_CLIENT_ID'client_secret = 'YOUR_CLIENT_SECRET'token_url = 'https://go.tallyfy.com/oauth/token'
payload = { 'grant_type': 'client_credentials', 'client_id': client_id, 'client_secret': client_secret, 'scope': '*'}
response = requests.post(token_url, data=payload)
if response.status_code == 200: token_data = response.json() print("Access token:", token_data['access_token'])else: print(f"Error: {response.status_code}") print(response.text)import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.net.URLEncoder;import java.nio.charset.StandardCharsets;import java.util.Map;import java.util.stream.Collectors;
public class TallyfyClientCredentials { public static void main(String[] args) throws Exception { String tokenUrl = "https://go.tallyfy.com/oauth/token";
Map<String, String> formData = Map.of( "grant_type", "client_credentials", "client_id", "YOUR_CLIENT_ID", "client_secret", "YOUR_CLIENT_SECRET", "scope", "*" );
String form = formData.entrySet().stream() .map(e -> e.getKey() + "=" + URLEncoder.encode( e.getValue(), StandardCharsets.UTF_8)) .collect(Collectors.joining("&"));
HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(tokenUrl)) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(form)) .build();
HttpResponse<String> response = client.send( request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() == 200 ? "Success: " + response.body() : "Error " + response.statusCode() + ": " + response.body()); }}package main
import ( "fmt" "io" "net/http" "net/url" "strings")
func main() { tokenUrl := "https://go.tallyfy.com/oauth/token"
data := url.Values{} data.Set("grant_type", "client_credentials") data.Set("client_id", "YOUR_CLIENT_ID") data.Set("client_secret", "YOUR_CLIENT_SECRET") data.Set("scope", "*")
req, err := http.NewRequest("POST", tokenUrl, strings.NewReader(data.Encode())) if err != nil { fmt.Println("Error:", err) return } req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body) fmt.Printf("Status %d: %s\n", resp.StatusCode, string(body))}#include <iostream>#include <string>#include <cpprest/http_client.h>
using namespace web;using namespace web::http;using namespace web::http::client;
int main() { http_client client(U("https://go.tallyfy.com/oauth/token"));
uri_builder builder; builder.append_query(U("grant_type"), U("client_credentials")); builder.append_query(U("client_id"), U("YOUR_CLIENT_ID")); builder.append_query(U("client_secret"), U("YOUR_CLIENT_SECRET")); builder.append_query(U("scope"), U("*"));
http_request request(methods::POST); request.headers().set_content_type( U("application/x-www-form-urlencoded")); request.set_body(builder.query());
client.request(request) .then([](http_response response) { return response.extract_json(); }) .then([](json::value body) { std::wcout << body.serialize() << std::endl; }) .wait();
return 0;}// Requires C++ REST SDK (Casablanca)using System;using System.Collections.Generic;using System.Net.Http;using System.Threading.Tasks;
public class TallyfyAuth{ private static readonly HttpClient client = new HttpClient();
public static async Task Main() { var tokenUrl = "https://go.tallyfy.com/oauth/token";
var formContent = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>( "grant_type", "client_credentials"), new KeyValuePair<string, string>( "client_id", "YOUR_CLIENT_ID"), new KeyValuePair<string, string>( "client_secret", "YOUR_CLIENT_SECRET"), new KeyValuePair<string, string>("scope", "*") });
var response = await client.PostAsync(tokenUrl, formContent); var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(response.IsSuccessStatusCode ? $"Success: {body}" : $"Error {response.StatusCode}: {body}"); }}Response:
{ "token_type": "Bearer", "expires_in": 604800, "access_token": "eyJ0eXAiOiJKV1Q..."}3. Provision users (optional)
Section titled “3. Provision users (optional)”With your application token, you can create users in your organization:
- Endpoint:
POST https://go.tallyfy.com/api/applications/{orgID}/users - Headers:
Authorization: Bearer {your_app_access_token}Content-Type: application/jsonX-Tallyfy-Client: APIClient
- Body fields:
first_name(required): Max 32 characterslast_name(required): Max 32 charactersemail(required): Must be unique, valid domainrole(optional):admin,standard, orlighttimezone(optional): User’s timezone string
4. Get user-specific tokens (optional)
Section titled “4. Get user-specific tokens (optional)”To act as a specific user, generate a user-scoped token with your application token.
- Endpoint:
POST https://go.tallyfy.com/api/applications/{orgID}/users/{email}/token - Headers:
Authorization: Bearer {your_app_access_token}X-Tallyfy-Client: APIClient
- Note: No request body needed - the user’s email goes in the URL path.
Response:
{ "token_type": "Bearer", "expires_in": 7776000, "access_token": "eyJ0eXAiOiJKV1Q..."}5. Make API requests
Section titled “5. Make API requests”Use your token (application-level or user-specific) in API calls with these required headers:
Authorization: Bearer {token}Accept: application/jsonX-Tallyfy-Client: APIClient
All API endpoints follow the pattern https://go.tallyfy.com/api/organizations/{orgID}/...
For code examples of specific API operations, see the personal access token guide - the request format is identical, just swap in your token.
Security best practices
Section titled “Security best practices”- Store client credentials in encrypted secrets management (never in source code)
- Protect both application-level and user-specific tokens
- Rotate secrets periodically
- Use HTTPS for all requests
- Re-request application tokens before the 7-day expiry
Related articles
Section titled “Related articles”Code Samples > Authentication methods
Open Api > API integration guide
Open Api > OAuth authorization flow for third-party applications
Was this helpful?
- 2026 Tallyfy, Inc.
- Privacy Policy
- Terms of Use
- Report Issue
- Trademarks