Read activity, connection history, and billing events#
The REST API exposes the team activity history and connection history shown in the Teams dashboard's Activity Logs and Connection Logs, plus the team's billing ledger. Use these endpoints to archive or analyze logs and billing changes, for example to export each day's connections to CSV or to retain a computer's connection history before removing the computer deletes it.
| Data | Endpoint | Teams dashboard |
|---|---|---|
| Team activity (users, computers, settings, billing changes) | GET /v1/team/{teamID}/history |
Activity Logs |
| Connection history for the team or selected computers | GET /v1/team/{teamID}/history/devices |
Connection Logs |
| Connection history for one computer (deprecated) | GET /v1/team/{teamID}/device/{deviceID}/history |
|
| Billing ledger | GET /v1/team/{teamID}/billing/events |
Applies to#
- A Jump Desktop for Teams team, and an API token from a team Admin or Admin (Read-Only). Every request on this page is a
GET, so read-only tokens are sufficient. See Get started with the REST API for tokens, team IDs, and error responses. - Samples use placeholder IDs
T-EXAMPLE123(team) andD-EXAMPLE456(computer), and read the token fromJUMP_API_TOKEN. - Python samples use the
requestspackage. JavaScript samples require Node.js 18 or later and use top-levelawait, so save them as.mjsfiles.
Time ranges and paging#
All three history endpoints accept the same query parameters:
| Parameter | Meaning |
|---|---|
startTime |
Start of the range, in seconds since the Unix epoch. Inclusive. |
stopTime |
End of the range, in seconds since the Unix epoch. Inclusive. |
desc |
true returns the newest events first, false the oldest first. |
limit |
Number of events to return per request. |
offset |
Number of events to skip. |
Set limit and desc explicitly instead of relying on default values.
The two team endpoints return hasMoreEvents. When hasMoreEvents is true, repeat the request with offset set to the returned nextOffset. For a stable export, use a fixed time range and desc=false.
Timestamps in responses use different units:
| Where | Field | Unit |
|---|---|---|
| Activity events | header.eventTimestampUnixNano |
Nanoseconds |
| Connection events | timestamp |
Milliseconds |
| Billing ledger entries | timestamp |
Seconds |
Read team activity history#
GET /v1/team/{teamID}/history returns activity entries in the events array. Each entry has a header and one field naming the event type, such as teamUserAddedEvent, teamDeviceRemovedEvent, teamUserLoggedInEvent, or teamSettingsChangedEvent. The header contains the event time and, where recorded, tokenInfo identifying who made the change. The REST API reference lists every event type.
The following sample prints the 50 most recent events:
import os
import requests
resp = requests.get(
"https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history",
headers={"Authorization": f"Bearer {os.environ['JUMP_API_TOKEN']}"},
params={"limit": 50, "desc": "true"},
)
if not resp.ok:
raise SystemExit(f"{resp.status_code} {resp.text}")
body = resp.json()
for entry in body.get("events") or []:
nanos = entry.get("header", {}).get("eventTimestampUnixNano", 0)
kinds = [key for key in entry if key != "header"]
print(nanos // 1_000_000_000, ", ".join(kinds))
print("More events:", body.get("hasMoreEvents"), "next offset:", body.get("nextOffset"))
const res = await fetch(
"https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history?limit=50&desc=true",
{ headers: { Authorization: `Bearer ${process.env.JUMP_API_TOKEN}` } },
);
if (!res.ok) {
console.error(res.status, await res.text());
process.exit(1);
}
const body = await res.json();
for (const entry of body.events ?? []) {
const seconds = Math.floor((entry.header?.eventTimestampUnixNano ?? 0) / 1e9);
const kinds = Object.keys(entry).filter((key) => key !== "header");
console.log(seconds, kinds.join(", "));
}
console.log("More events:", body.hasMoreEvents, "next offset:", body.nextOffset);
Invoke-RestMethod throws on a non-2xx response.
$headers = @{ Authorization = "Bearer $env:JUMP_API_TOKEN" }
$uri = "https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history?limit=50&desc=true"
try {
$body = Invoke-RestMethod -Uri $uri -Headers $headers
} catch {
Write-Error "$($_.Exception.Response.StatusCode.value__) $($_.ErrorDetails.Message)"
exit 1
}
foreach ($entry in $body.events) {
$kinds = $entry.PSObject.Properties.Name | Where-Object { $_ -ne "header" }
"{0} {1}" -f [math]::Floor($entry.header.eventTimestampUnixNano / 1e9), ($kinds -join ", ")
}
Read connection history#
GET /v1/team/{teamID}/history/devices returns connection events in the deviceEvents array. To include every computer on the team, omit ids; to select computers, repeat ids once per computer (ids=D-EXAMPLE456&ids=D-EXAMPLE457).
Each event has id, timestamp, deviceID, computerHostName, and one of these fields:
| Field | Emitted when |
|---|---|
incomingConnectionRequest |
A connection is requested, before it is established. |
incomingConnectionEvent |
A connection is established. |
authSucceededEvent |
Authentication for a Fluid connection succeeds. |
authFailedEvent |
Authentication for a Fluid connection fails. Includes reason. |
connectionClosedEvent |
A connection closes. |
All events of one connection share the same connectionID. Connection details include the following fields:
tunnelID:fluid,rdp,vnc, orunknown.sourceType:cloud,directip, orunknown.peerInfo: the connecting user'semailandipAddress.
Authentication and close events carry these details in associatedIncomingConnectionEvent.
The length of history displayed in Connection Logs in the Teams dashboard is planFeatures.deviceEventsMaxRetrievalDuration (in seconds) in team information. Removing a computer from the team deletes its connection history, so export the history before removing the computer.
The following sample reads the last 24 hours of connection history for one computer:
import os
import time
import requests
now = int(time.time())
resp = requests.get(
"https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history/devices",
headers={"Authorization": f"Bearer {os.environ['JUMP_API_TOKEN']}"},
params={"ids": ["D-EXAMPLE456"], "startTime": now - 86400, "stopTime": now, "limit": 100},
)
resp.raise_for_status()
for event in resp.json().get("deviceEvents") or []:
print(event["timestamp"], event["deviceID"], event.get("computerHostName"))
const now = Math.floor(Date.now() / 1000);
const params = new URLSearchParams({ startTime: now - 86400, stopTime: now, limit: 100 });
params.append("ids", "D-EXAMPLE456");
const res = await fetch(
`https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history/devices?${params}`,
{ headers: { Authorization: `Bearer ${process.env.JUMP_API_TOKEN}` } },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const body = await res.json();
for (const event of body.deviceEvents ?? []) {
console.log(event.timestamp, event.deviceID, event.computerHostName);
}
$headers = @{ Authorization = "Bearer $env:JUMP_API_TOKEN" }
$now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$uri = "https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history/devices" +
"?ids=D-EXAMPLE456&startTime=$($now - 86400)&stopTime=$now&limit=100"
(Invoke-RestMethod -Uri $uri -Headers $headers).deviceEvents | Select-Object timestamp, deviceID, computerHostName
One computer (deprecated endpoint)#
GET /v1/team/{teamID}/device/{deviceID}/history is deprecated. Use GET /v1/team/{teamID}/history/devices with ids instead. The deprecated endpoint differs as follows:
- Events are in
events, withoutdeviceID. - The response has no
hasMoreEventsornextOffset. To page through results, increaseoffsetuntil a response contains no events. descdefaults tofalse.
Export yesterday's connection history to CSV#
The following Python script uses requests to write every connection event from yesterday (UTC) to connections.csv. The script follows nextOffset until hasMoreEvents is false. It only reads data from the REST API, and it overwrites connections.csv if the file exists.
import csv
import datetime
import os
import requests
URL = "https://api.jumpdesktop.com/v1/team/T-EXAMPLE123/history/devices"
HEADERS = {"Authorization": f"Bearer {os.environ['JUMP_API_TOKEN']}"}
EVENT_TYPES = [
"incomingConnectionRequest",
"incomingConnectionEvent",
"authSucceededEvent",
"authFailedEvent",
"connectionClosedEvent",
]
FIELDS = [
"time_utc", "device_id", "computer_host_name", "event", "connection_id",
"protocol", "user_email", "ip_address", "local_user_name", "failure_reason",
]
# Yesterday, 00:00:00 to 23:59:59 UTC. stopTime is inclusive.
today = datetime.datetime.now(datetime.timezone.utc).replace(
hour=0, minute=0, second=0, microsecond=0
)
start = int((today - datetime.timedelta(days=1)).timestamp())
stop = int(today.timestamp()) - 1
def fetch_page(offset):
params = {"startTime": start, "stopTime": stop, "desc": "false", "limit": 1000, "offset": offset}
resp = requests.get(URL, headers=HEADERS, params=params)
resp.raise_for_status()
return resp.json()
def to_row(event):
kind = next((k for k in EVENT_TYPES if k in event), "")
detail = event.get(kind) or {}
connection = detail.get("associatedIncomingConnectionEvent", detail)
peer = connection.get("peerInfo") or {}
direct = connection.get("directConnectionInfo") or {}
return {
"time_utc": datetime.datetime.fromtimestamp(
event["timestamp"] / 1000, datetime.timezone.utc
).isoformat(),
"device_id": event.get("deviceID", ""),
"computer_host_name": event.get("computerHostName", ""),
"event": kind,
"connection_id": detail.get("connectionID", ""),
"protocol": connection.get("tunnelID", ""),
"user_email": peer.get("email", ""),
"ip_address": peer.get("ipAddress") or direct.get("ipaddress", ""),
"local_user_name": detail.get("localUserName", ""),
"failure_reason": detail.get("reason", ""),
}
with open("connections.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=FIELDS)
writer.writeheader()
offset = 0
while True:
page = fetch_page(offset)
for event in page.get("deviceEvents") or []:
writer.writerow(to_row(event))
if not page.get("hasMoreEvents"):
break
offset = page["nextOffset"]
To verify the export, open connections.csv and compare several rows with Connection Logs in the Teams dashboard for the same day.
Read the billing ledger#
GET /v1/team/{teamID}/billing/events returns the most recent team events affecting the billable amount, such as successful or failed payments, subscription updates or cancellations, user count updates, and user additions or removals. The response's events array contains the entries, newest first. Each entry has id, teamid, timestamp, and event; event contains one field naming the event type.
The published OpenAPI specification lists this endpoint as /teams/{teamid}/billing/events with an array response. The service uses the /v1/team/ path shown here and returns an object with an events array.
Related#
- REST API reference
- Manage computers with the REST API
- Activity Logs and Connection Logs: view and export the same logs in the Teams dashboard.
- Security best practices