Skip to content

The caller's denied certificate requests

GET
/api/decisions/denied
curl --request GET \
--url https://example.com/api/decisions/denied \
--header 'X-Ssoossh-Key-Secret: <X-Ssoossh-Key-Secret>'

The half of a decision history that issues nothing. /api/certs reads the certificates table, and a denial never produces a row in it, so denials are served here and the client interleaves the two by time.

Scoped by the caller’s own decision records, with no parameter to widen it. Uses cursor-based pagination (not offset).

after
string

Decision ID to start after (cursor)

limit
integer

Maximum denials to return (default 25, max 100)

q
string

Search over request ID and the reported host context

type
string

Filter by certificate type (user/service/pam/console)

status
string

Live or expired; a denial is neither, so either returns nothing

Denials, newest first, with cursor for next page

Media typeapplication/json
object
data
required
object
denials
required
Array<object>
object
certificate_request_id
required

CertificateRequestID is the request that was refused.

string
client
string
decided_at
required
string
decided_source_ip

DecidedSourceIP is the address the denial was made from – the decider’s browser, server-observed, not anything the requester claimed.

string
id
required

ID is the decision’s id, not a certificate’s – there is no certificate. It is what the audit event for this denial carries.

string
pam_service

The rest of the compact host-context snapshot the decision row carries, and claims in exactly the same way: PAMService, TTY and RemoteHost are what a PAM or console request said it was doing, and Client is what a user request reported instead, since it has no PAM service or terminal.

Present because “I refused a sudo on rack07 from 10.1.2.9” is a memory a month later and “I refused request 4f2a” is not. All of it is copied onto the decision at decision time, so unlike the request’s own columns it cannot go blank later.

string
remote_host
string
reported_hostname
string
reported_username

ReportedUsername and ReportedHostname are the “user@host” the request claimed for itself, self-reported by an unauthenticated caller and never verified. They are here because they are what makes a denial identifiable a month later: “I refused a console login on rack07” is a memory, “I refused request 4f2a” is not.

string
tty
string
type

Type is what was asked for. Empty when the request row behind the decision is gone: the decisions table is the permanent one by design, so the client must render a row whose type it does not know.

string
Allowed values: user service pam console
next_cursor
string
error
string

Example

{
"data": {
"denials": [
{
"type": "user"
}
]
}
}

Invalid limit or cursor

Media typeapplication/json
object
data

Data is always null on an error response.

error
required

Error is a human-readable message. Branch on the HTTP status rather than this string; it is for humans and logs.

string
error_code
required

ErrorCode is a machine-readable error classifier. One of the error_code constants from internal/apitypes; use this to decide whether to retry or branch on the failure mode.

string

Example

{
"error": "certificate request \"9f1c…\" not found",
"error_code": "not_found"
}

No valid session

Media typeapplication/json
object
data

Data is always null on an error response.

error
required

Error is a human-readable message. Branch on the HTTP status rather than this string; it is for humans and logs.

string
error_code
required

ErrorCode is a machine-readable error classifier. One of the error_code constants from internal/apitypes; use this to decide whether to retry or branch on the failure mode.

string

Example

{
"error": "certificate request \"9f1c…\" not found",
"error_code": "not_found"
}