Open Api > API usage as a third-party application instead of a user
Use the client credentials flow
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.
- 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)
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
Contact Tallyfy Support with your integration use case. They’ll provide a Client ID and Client Secret for your organization. Store these securely.
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..."}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
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..."}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.
- 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
Code Samples > Authentication methods
Open Api > API integration guide
Was this helpful?
- 2025 Tallyfy, Inc.
- Privacy Policy
- Terms of Use
- Report Issue
- Trademarks