SSH Sessions
The SSH Sessions resource provides read-only access to the SSH session activity recorded by Keystash's SSH Connection Logging. Each session captures who connected to a Host Server, from where, how they authenticated and how long they stayed — the same activity you can browse in the app under SSH Sessions. Access to this resource requires the SSH Sessions → View Role permission (see Roles & Permissions).
Note
These endpoints are read-only — there is no create, update or delete.
List SSH Sessions
Returns the company's SSH sessions, newest first, with optional filtering and paging. With no parameters supplied you get the most recent sessions, up to the default limit.
Path: /ssh-sessions/list
Method: GET
Authentication: Bearer Token
Request Headers
| Key | Value | Required |
|---|---|---|
Accept-Encoding |
gzip | optional |
Request Parameters
All parameters are optional query parameters. Malformed filter values are silently dropped, so a bad value yields more results, not an error.
| Key | Data Type | Required | Notes |
|---|---|---|---|
start_date |
string | optional | Inclusive lower bound on the session start, in UTC YYYY-MM-DD HH:mm:ss. Must be supplied together with end_date — a lone date is ignored. |
end_date |
string | optional | Inclusive upper bound on the session start, in UTC YYYY-MM-DD HH:mm:ss. Together with start_date, filters on started_at between the two dates. |
user_ids |
array of string | optional | Filter to sessions for these Keystash users, by their id — the user.id field in a response, and the id a Users API call returns. Each value must be 1–16 alphanumeric characters or the whole filter is dropped. See the array note below. |
server_ids |
array of string | optional | Filter by server, by its id — the server.id field in a response. Each value must be 1–16 alphanumeric characters or the whole filter is dropped. See the array note below. |
server_group_ids |
array of string | optional | Filter by Server Group, by its id — the server.server_group_id field in a response. Same array and id validation as server_ids. |
statuses |
array of string | optional | Filter by session status label: Incomplete, Failed, Active or Ended (case-insensitive, surrounding whitespace trimmed). A single unrecognised label drops the whole filter. See the array note below. |
limit |
integer | optional | Maximum number of rows to return. Defaults to 10000 and is limited to the range 1–10000. |
offset |
integer | optional | Row offset for paging. Defaults to 0 and is limited to 0 or greater. |
Note
The array filters — user_ids, server_ids, server_group_ids and statuses — must be sent as repeated, bracketed query parameters, for example ?statuses[]=Ended&statuses[]=Active or ?server_ids[]=DmcEgixUXpWVv7tk. A bare ?statuses=Ended arrives as a scalar string, fails the array check and is ignored — even for a single value. Always use the bracketed form, as shown in the example request.
Request Body
None.
Response Fields
A successful response is a JSON array of session objects. Each object has these fields:
| Field | Type | Notes |
|---|---|---|
id |
string | The session's Keystash id. Pass this to View SSH Session as the id parameter. |
session_id |
string | The agent/PAM session identifier the host reported. Unique per session on a given server. |
status |
string | The session status label: Incomplete, Failed, Active or Ended (or Unknown if unmapped). This is the same label accepted by the statuses filter. See Session Statuses. |
user |
object | The resolved Keystash user. The user fields are null if the user could not be resolved or was later deleted. |
user.id |
string|null | The Keystash user id. null for unresolved or root logins. Use it in the user_ids filter. |
user.name |
string|null | The user's full name (firstname lastname). |
user.email |
string|null | The user's email / Keystash username. |
user.server_username |
string|null | The Linux username the session logged in as. |
server |
object | The target server. |
server.id |
string|null | The server id. Use it in the server_ids filter. |
server.hostname |
string|null | The server hostname. |
server.server_group_id |
string|null | The server's Server Group id. Use it in the server_group_ids filter. |
server.server_group_name |
string|null | The Server Group name. |
ip_address |
string | The source IP address the user connected from. |
auth_method |
string|null | The raw authentication-method token (see Authentication Methods). unknown when not reported. |
auth_method_label |
string | The human label for auth_method. |
mfa_method |
string|null | The raw MFA-method token (see MFA Methods). |
mfa_method_label |
string | The human label for mfa_method. |
started_at |
string | UTC timestamp of the first event — the session start. |
connected_at |
string|null | UTC timestamp of connect; null if the session never connected. |
disconnected_at |
string|null | UTC timestamp of disconnect; null while the session is still Active or never completed. |
duration_seconds |
integer|null | The connect-to-disconnect duration in seconds; null while the session is Active, Failed or Incomplete. |
deny_reason |
string|null | The raw deny-reason token; null unless the session was denied (see Deny Reasons). |
deny_reason_label |
string | The human label for deny_reason (empty string when none). |
Note
An empty result is a 200 OK with an empty array ([]), the list simply returns no matches.
Example Request
curl --location --request GET 'https://app.keystash.io/api/v1/ssh-sessions/list?statuses[]=Ended&limit=50' \
--header 'Authorization: Bearer <token>'
Example Response
The example below shows one Ended session (with duration_seconds and disconnected_at populated) and one Active session (still connected, so those fields are null).
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{
"id": "x0Uf09z3TYjoNVFv",
"session_id": "sshd-8f3c1a2b9d4e",
"status": "Ended",
"user": {
"id": "bkQV8I5BRcAVpg1d",
"name": "Max Smith",
"email": "max.smith@example.org",
"server_username": "maxsmith"
},
"server": {
"id": "DmcEgixUXpWVv7tk",
"hostname": "web-01.hostingprovider.net",
"server_group_id": "BD720EP87kNORRcH",
"server_group_name": "Production Servers"
},
"ip_address": "192.168.1.58",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"started_at": "2026-06-22 09:14:00",
"connected_at": "2026-06-22 09:14:02",
"disconnected_at": "2026-06-22 09:25:11",
"duration_seconds": 669,
"deny_reason": null,
"deny_reason_label": ""
},
{
"id": "p8CduXmyw6n9dzdd",
"session_id": "sshd-1b7e44c0aa21",
"status": "Active",
"user": {
"id": "tr568I5BRcAVpwx7",
"name": "Anne Teak",
"email": "anne.teak@example.org",
"server_username": "anneteak"
},
"server": {
"id": "DmcEgixUXpWVv7tk",
"hostname": "web-01.hostingprovider.net",
"server_group_id": "BD720EP87kNORRcH",
"server_group_name": "Production Servers"
},
"ip_address": "192.168.1.59",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "none",
"mfa_method_label": "None",
"started_at": "2026-06-22 11:02:40",
"connected_at": "2026-06-22 11:02:41",
"disconnected_at": null,
"duration_seconds": null,
"deny_reason": null,
"deny_reason_label": ""
}
]
A Failed session looks like the object below — it never connected, so the connect, disconnect and duration fields are null, and the deny_reason/deny_reason_label pair explains why it was refused.
{
"id": "H6LPlSC081GIqzW0",
"session_id": "sshd-c92f0d7e6b15",
"status": "Failed",
"user": {
"id": "bkQV8I5BRcAVpg1d",
"name": "Max Smith",
"email": "max.smith@example.org",
"server_username": "maxsmith"
},
"server": {
"id": "DmcEgixUXpWVv7tk",
"hostname": "web-01.hostingprovider.net",
"server_group_id": "BD720EP87kNORRcH",
"server_group_name": "Production Servers"
},
"ip_address": "203.0.113.42",
"auth_method": "password",
"auth_method_label": "Password",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"started_at": "2026-06-22 08:47:13",
"connected_at": null,
"disconnected_at": null,
"duration_seconds": null,
"deny_reason": "bad_code",
"deny_reason_label": "Invalid 2FA code"
}
Example Error Response
Missing Permission (403 Forbidden)
HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=utf-8
{
"error": {
"code": "Forbidden",
"message": "You do not have the required permission to access this path.",
"details": [
{
"path": "/api/v1/ssh-sessions/list"
}
]
}
}
Database Error (500 Internal Server Error)
HTTP/1.1 500 Internal Server Error
Content-Type: application/json; charset=utf-8
{
"error": {
"code": "DataRetrievalError",
"message": "There was a problem retrieving the data from the database.",
"details": []
}
}
View SSH Session
Returns a single session together with its ordered authentication-event timeline.
Path: /ssh-sessions/view
Method: GET
Authentication: Bearer Token
Request Headers
| Key | Value | Required |
|---|---|---|
Accept-Encoding |
gzip | optional |
Request Parameters
| Key | Data Type | Required | Notes |
|---|---|---|---|
id |
string | required | The session id — the id field from a List SSH Sessions response. A missing or empty value returns 400 BadArgument; an unknown id returns 404 NotFound. |
Request Body
None.
Response Fields
A successful response is a single object with two fields:
| Field | Type | Notes |
|---|---|---|
session |
object | The session, with exactly the same fields as a List SSH Sessions entry. |
events |
array of object | The session's authentication events, oldest first. |
Each object in events has these fields:
| Field | Type | Notes |
|---|---|---|
id |
string | The agent-assigned unique id for this event. |
created |
string | UTC timestamp of when the event occurred, in YYYY-MM-DD HH:mm:ss second precision. Events are returned oldest first. |
event_type |
string | The raw event token: attempt, authenticate, connect, disconnect or deny (see Event Types). |
auth_method |
string|null | The raw authentication-method token for this event (see Authentication Methods). |
auth_method_label |
string | The human label for auth_method. |
mfa_method |
string|null | The raw MFA-method token for this event (see MFA Methods). |
mfa_method_label |
string | The human label for mfa_method. |
reason |
string|null | The raw deny/result reason token; populated on deny events (see Deny Reasons). |
reason_label |
string | The human label for reason. |
Example Request
curl --location --request GET 'https://app.keystash.io/api/v1/ssh-sessions/view?id=x0Uf09z3TYjoNVFv' \
--header 'Authorization: Bearer <token>'
Example Response
A clean Ended session traces from first attempt through to disconnect:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"session": {
"id": "x0Uf09z3TYjoNVFv",
"session_id": "sshd-8f3c1a2b9d4e",
"status": "Ended",
"user": {
"id": "bkQV8I5BRcAVpg1d",
"name": "Max Smith",
"email": "max.smith@example.org",
"server_username": "maxsmith"
},
"server": {
"id": "DmcEgixUXpWVv7tk",
"hostname": "web-01.hostingprovider.net",
"server_group_id": "BD720EP87kNORRcH",
"server_group_name": "Production Servers"
},
"ip_address": "192.168.1.58",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"started_at": "2026-06-22 09:14:00",
"connected_at": "2026-06-22 09:14:02",
"disconnected_at": "2026-06-22 09:25:11",
"duration_seconds": 669,
"deny_reason": null,
"deny_reason_label": ""
},
"events": [
{
"id": "a1B2c3D4e5F6g7H8",
"created": "2026-06-22 09:14:00",
"event_type": "attempt",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "none",
"mfa_method_label": "None",
"reason": null,
"reason_label": ""
},
{
"id": "i9J0k1L2m3N4o5P6",
"created": "2026-06-22 09:14:01",
"event_type": "authenticate",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"reason": null,
"reason_label": ""
},
{
"id": "q7R8s9T0u1V2w3X4",
"created": "2026-06-22 09:14:02",
"event_type": "connect",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"reason": null,
"reason_label": ""
},
{
"id": "y5Z6a7B8c9D0e1F2",
"created": "2026-06-22 09:25:11",
"event_type": "disconnect",
"auth_method": "pubkey",
"auth_method_label": "Public Key",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"reason": null,
"reason_label": ""
}
]
}
A Failed session ends on a deny event whose reason/reason_label records why it was refused:
{
"session": {
"id": "H6LPlSC081GIqzW0",
"session_id": "sshd-c92f0d7e6b15",
"status": "Failed",
"user": {
"id": "bkQV8I5BRcAVpg1d",
"name": "Max Smith",
"email": "max.smith@example.org",
"server_username": "maxsmith"
},
"server": {
"id": "DmcEgixUXpWVv7tk",
"hostname": "web-01.hostingprovider.net",
"server_group_id": "BD720EP87kNORRcH",
"server_group_name": "Production Servers"
},
"ip_address": "203.0.113.42",
"auth_method": "password",
"auth_method_label": "Password",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"started_at": "2026-06-22 08:47:13",
"connected_at": null,
"disconnected_at": null,
"duration_seconds": null,
"deny_reason": "bad_code",
"deny_reason_label": "Invalid 2FA code"
},
"events": [
{
"id": "g3H4i5J6k7L8m9N0",
"created": "2026-06-22 08:47:13",
"event_type": "attempt",
"auth_method": "password",
"auth_method_label": "Password",
"mfa_method": "none",
"mfa_method_label": "None",
"reason": null,
"reason_label": ""
},
{
"id": "o1P2q3R4s5T6u7V8",
"created": "2026-06-22 08:47:15",
"event_type": "deny",
"auth_method": "password",
"auth_method_label": "Password",
"mfa_method": "totp_mfa",
"mfa_method_label": "TOTP",
"reason": "bad_code",
"reason_label": "Invalid 2FA code"
}
]
}
Example Error Response
Missing Session ID (400 Bad Request)
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
{
"error": {
"code": "BadArgument",
"message": "A valid session id is required.",
"details": []
}
}
Session Not Found (404 Not Found)
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
{
"error": {
"code": "NotFound",
"message": "Your request was successfully processed but there was no data to be returned.",
"details": []
}
}
Understanding the Values
Both endpoints use the same set of consistent labels for statuses, methods, events and deny reasons. The tables below explain what each one means. They mirror the labels shown in the app's SSH Sessions view.
Session Statuses
The status response field returns the label string, and the statuses filter accepts those same label strings (case-insensitive).
| Label | Meaning |
|---|---|
| Incomplete | Only attempt or authenticate events were seen — the session never connected. |
| Failed | A deny event occurred. |
| Active | Connected, with no disconnect yet. |
| Ended | A disconnect was received (or the session was swept after 72 hours stale-Active — see SSH Sessions). |
Authentication Methods
These apply to the auth_method / auth_method_label fields.
| Token | Label |
|---|---|
pubkey |
Public Key |
password |
Password |
keyboard-interactive |
Keyboard Interactive |
gssapi |
GSSAPI |
hostbased |
Host Based |
unknown |
Not Reported |
MFA Methods
These apply to the mfa_method / mfa_method_label fields.
| Token | Label |
|---|---|
none |
None |
totp_mfa |
TOTP |
totp_mfa_scratch |
TOTP Scratch Code |
Event Types
These apply to the events[].event_type field. The API emits the raw token only; the meanings are below.
| Token | Meaning |
|---|---|
attempt |
An authentication attempt was made. |
authenticate |
Authentication succeeded. |
connect |
The session connected. |
disconnect |
The session disconnected. |
deny |
The session was denied (see reason). |
Deny Reasons
These apply to the deny_reason / reason tokens and their *_label fields. Any token not listed below is humanised automatically — underscores become spaces and the result is title-cased.
| Token | Label |
|---|---|
not_authorized |
Not authorized |
unenrolled_denied |
Denied - User not enrolled in 2FA |
not_enrolled |
Not enrolled in 2FA |
bad_code |
Invalid 2FA code |
rate_limited |
Rate limited |
replay |
2FA code already used |
attempts_exhausted |
Too many failed attempts |
mfa_method_unknown |
Unknown MFA method |
agent_unavailable_deny |
Agent unavailable — denied |
agent_unavailable_allow |
Agent unavailable — allowed |
Related Documentation
- SSH Sessions — viewing, filtering and exporting sessions in the app.
- SSH Connection Logging — how the Keystash Agent captures sessions and how to enable logging per Server Group.
- Roles & Permissions — assigning the SSH Sessions → View permission that these endpoints require.
- Authentication — obtaining the Bearer token used in the examples above.
- Introduction — the API base URL, rate limits, dates and times and the standard error envelope.