Skip to content

Using the REST API

  • Before you can use the TMM REST API, you must first Request an Application ID for your application.
  • Your Application ID uniquely identifies your application.
  • Your Application ID comes int the form of a UUID.
  • Do not share your Application ID with anyone outside of your development team.

TMM API access codes are used to authorize applications to access the TMM REST API. The client application generates an access code and includes it in the HTTP Authorization header of the request. TMM will validate the access code against all registered client applications. If the code checks out, then TMM will process the request. If the code does not check out, then TMM will respond with 401 Unauthorized.

New integrators should use Access Code V2. We will continue to support Access Code V1 for existing integrations.

The following access codes are available:

Access Code V2

  • Recommended for all new integrations.
  • A long lived token, valid for 60 minutes
  • For secure connections, can be used multiple times until it expires.
  • For insecure connections, can be used only once.
  • Automatically registers the client application with TMM REST API server when the access code is first used.
  • Can be used with native applications and web applications.

Access Code V1

  • Continues to be supported for existing integrations.
  • A short lived token, valid for 1 second
  • Requires a manual client application registration request to be accepted.
  • Can be used with native applications only.
  • Requires client application registration before use.
  • Access Code V1 is generated by formatting your Application ID with the current UTC time, hashing the result with SHA256, and encoding the hash in Base64.
  • TMM will check the access code against all registered client applications. If the code checks out, then TMM will process the request. If the code does not check out, then TMM will respond with 401 Unauthorized.
  • The access code is valid for 1 second, so it must be generated at the time of the request.
  • When debugging, avoid setting a breakpoint between generating the access code and sending the request, as this will cause the access code to expire before it is sent.
public static string GenerateAccessCode(string appID, DateTime utcTime)
{
string lowercaseID = appID.ToLowerInvariant();
// Format utcTime as an ISO8601 compliant string, like this:
// 2024-03-15T18:42:31Z
string iso8601Time = utcTime.ToString("yyyy-MM-dd'T'HH:mm:ssK", CultureInfo.InvariantCulture);
string plaintextAccessCode = lowercaseID + iso8601Time;
byte[] utf8Bytes = Encoding.UTF8.GetBytes(plaintextAccessCode);
byte[] hashedBytes = SHA256.HashData(utf8Bytes);
string base64String = Convert.ToBase64String(hashedBytes);
return base64String;
}

To test and verify your access code generation code, we have provided you with access-code-gold-file.json, which contains a set of random ApplicationID + UTC Time and the access code they should generate. The data looks like this:

{
"ApplicationID": "ce1e7a1e-3128-4f63-b829-223c7cb7ca1d",
"UtcTime": "2074-03-16T08:46:27Z",
"AccessCode": "BF4hkCZWjxyw2WN9xVPHj7gpyxrS3CRUX0BaXutzw14="
}

When you have generated an access code, insert it into the HTTP Authorization header, using the Basic scheme. Upon receiving the request, TMM will validate the Access Code against all registered client applications. If the Code checks out, then TMM will process the request. If the code does not check out, then TMM will respond with 401 Unauthorized.

Example Request:

GET api/v1/tmmInfo HTTP/1.1
Authorization: Basic BF4hkCZWjxyw2WN9xVPHj7gpyxrS3CRUX0BaXutzw14=
  • V2 access codes use public key encryption, your Application ID, and the current UTC time.
  • V2 access codes are valid for 60 minutes.
    • For secure connections, V2 access codes can be used multiple times until they expire.
    • For insecure connections, V2 access codes can be used only once.

In order to generate an Access Code V2, you must first retrieve the public key from the TMM REST API server. The public key is used to encrypt the plain text access code, which is then sent to the REST API server for validation.

  • TMM generates a public/private key pair that has the lifetime of the installed TMM application.
  • Query the public key from the TMM REST API server using the api/v1/publicKey endpoint.
  • The publicKey endpoint does not require an access code/Authorization header to be sent in the request, and is available to all applications.
  • The public key is returned in JSON Web Key (JWK) format, which is a standard format for representing public keys.

Example Response:

{
"kty": "RSA",
"n": "tjRGne4mEIqru489eq8BfxNpam8laClBzCbzZXsByWQZ09...",
"e": "AQAB"
}
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace TestApp.AccessCode;
static public class AccessCodeV2
{
private static RSA _rsa;
public static void SetPublicKey(string jwkJson)
{
ArgumentException.ThrowIfNullOrWhiteSpace(jwkJson);
using JsonDocument document = JsonDocument.Parse(jwkJson);
JsonElement root = document.RootElement;
if (root.GetProperty("kty").GetString() != "RSA")
{
throw new ArgumentException("Only RSA JWK keys are supported.", nameof(jwkJson));
}
byte[] modulus = Base64UrlDecode(root.GetProperty("n").GetString());
byte[] exponent = Base64UrlDecode(root.GetProperty("e").GetString());
var rsa = RSA.Create();
rsa.ImportParameters(new RSAParameters
{
Modulus = modulus,
Exponent = exponent,
});
_rsa?.Dispose();
_rsa = rsa;
}
private static byte[] Base64UrlDecode(string base64Url)
{
string base64 = base64Url.Replace('-', '+').Replace('_', '/');
int padding = (4 - base64.Length % 4) % 4;
return Convert.FromBase64String(base64.PadRight(base64.Length + padding, '='));
}
public static string Generate(Guid appID, DateTime utcTime)
{
if(_rsa is null)
{
throw new InvalidOperationException("public key not set");
}
string lowercaseID = appID.ToString("D").ToLowerInvariant();
// Format utcTime as an ISO8601 compliant string, like this: 2024-02-22T18:00:00Z
string iso8601Time = utcTime.ToString("yyyy-MM-dd'T'HH:mm:ssK", CultureInfo.InvariantCulture);
string plaintextAccessCode = lowercaseID + " " + iso8601Time;
byte[] utf8Bytes = Encoding.UTF8.GetBytes(plaintextAccessCode);
byte[] encryptedBytes = _rsa.Encrypt(utf8Bytes, RSAEncryptionPadding.OaepSHA256);
string base64String = Convert.ToBase64String(encryptedBytes);
return base64String;
}
}

When you have generated an access code, insert it into the HTTP Authorization header, using the AccessCodeV2 scheme. Upon receiving the request, TMM will validate the Access Code against all registered client applications. If the Code checks out, then TMM will process the request. If the code does not check out, then TMM will respond with 401 Unauthorized.

Example Request:

GET api/v1/tmmInfo HTTP/1.1
Authorization: AccessCodeV2 TXJxUhsje4ZgyzAOFT30MX0ipgm7zte++8+Zk7ZIUdheTe83mt0iCDbbHDFSMkGn7gM8Wd3tgZ7abQ8OgJRkIkUmeGKZehRuf09XclXfyLKm6kNA2YZJm2xVWLBE2TekOqWSSBATmR8IyeEXGztWKA7CRliUq3iIX3XRsV2b+n4ISV83pYkHrmWHq35nHcYitWEnB3X1rv1NdVbPU5NMxeBMuGNvsEI5Sv7/4WkOX1bZmUZmpIX/GQ9mWbeOKPqF5+TBOuyvWw9bnBgrseHGXvrJgKqaG3Hbcb35jLd3EZ1HPbwHlW9uBDi4+xfOCbReT0Kc84jdhj3TujXbGDNwCA==

In this example, we will use a secure connection and Access Code V2 to get information about the TMM application (see GET tmmInfo).

static async Task GetPublicKeyAsync()
{
string url = "https://tmm-api-local.fieldsystems.trimble.com:9638/api/v1/publicKey";
// Instantiate the HttpClient
using var client = new HttpClient();
try
{
Console.WriteLine("Getting public key...");
// No access code required for public key
// Make the GET request
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode(); // Throws if HTTP status is an error
// Read the response body as a string
string jwk = await response.Content.ReadAsStringAsync();
// Set the public key for AccessCodeV2 generation
AccessCodeV2.SetPublicKey(jwk);
// Parse as a generic JSON object (JsonNode/JsonObject)
// This allows you to navigate the JSON dynamically without a strongly-typed class.
JsonNode? jsonObject = JsonNode.Parse(jwk);
if (jsonObject != null)
{
// Print the formatted JSON to console
Console.WriteLine(jsonObject.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));
}
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
static async Task GetTmmInfoAsync(Guid appID)
{
string url = "https://tmm-api-local.fieldsystems.trimble.com:9638/api/v1/tmmInfo";
// Instantiate the HttpClient
using var client = new HttpClient();
try
{
// Generate a V2 access code
string accessCode = AccessCodeV2.Generate(appID, DateTime.UtcNow);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("AccessCodeV2", accessCode);
// Make the GET request
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode(); // Throws if HTTP status is an error
// Read the response body as a string
string responseBody = await response.Content.ReadAsStringAsync();
// Parse as a generic JSON object (JsonNode/JsonObject)
// This allows you to navigate the JSON dynamically without a strongly-typed class.
JsonNode? jsonObject = JsonNode.Parse(responseBody);
if (jsonObject != null)
{
// Print the formatted JSON to console
Console.WriteLine(jsonObject.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));
}
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}