Skip to content

View a certificate's full details

GET
/api/certs/{id}
curl --request GET \
--url https://example.com/api/certs/example \
--header 'X-Ssoossh-Key-Secret: <X-Ssoossh-Key-Secret>'

Returns a single certificate by ID if the caller is authorized: the user who approved the underlying request, or someone with auditor-level access. Returns 404 uniformly for “not found” and “not authorized” to not leak existence.

id
required
string

Certificate ID

Certificate details

Media typeapplication/json
object
data
required
object
critical_options

CriticalOptions are the options fixed into the certificate (force-command, source-address). Kept separate from Extensions because sshd treats them differently: it rejects a certificate carrying a critical option it does not understand, where an unknown extension is ignored. Populated by the detail endpoint alone, same as Extensions.

object
key
additional properties
string
decided_accept_language
string
decided_at
string
decided_by_email
string
decided_by_groups
Array<string>
decided_by_other_accounts
Array<string>
decided_by_outcome
string
decided_by_service_accounts
Array<string>
decided_by_subject
string
decided_by_username
string
decided_forwarded_for
string
decided_granted_options

Options are the certificate options fixed at approval, already narrowed by server config: what a redemption actually grants, not what the client asked for.

object
extensions
required
Array<string>
force_command
string
no_touch_required
required
boolean
source_addresses
Array<string>
decided_policy_explanation
string
decided_principals

The decision’s content and reasoning; see RequestDetailResponse. Populated on the detail endpoint only.

Array<string>
decided_source_ip
string
decided_user_agent
string
enrollment_id

EnrollmentID is the service code this certificate was redeemed from, so the UI can link to it. Present only on a service certificate.

string
expires_at
required
string
extensions

Extensions are the SSH certificate extensions the certificate was signed with (permit-pty, permit-agent-forwarding, …), decoded from the audit row’s JSON column rather than passed through as a string: the browser should not have to parse JSON out of JSON.

Populated by the detail endpoint alone. A list row does not display them, and carrying them on every row of a hundred-row page would be payload nobody reads.

Array<string>
id
required
string
issued_at
required
string
key_id
required
string
principals
required
string
public_key_fingerprint
required
string
reported_client
string
reported_hostname
string
reported_machine_id
string
reported_pam_service
string
reported_process
string
reported_remote_host
string
reported_requesting_user
string
reported_tty
string
reported_username

What asked for this certificate, as the requester reported it, taken from the decision record’s snapshot rather than from the request – see model.CertificateRequestDecision. Without these the history could say who approved a certificate and what it granted but not which machine or command it was for, which is the question an incident review actually starts from.

Every one is self-reported by an unauthenticated caller and must be rendered as a claim, exactly as the approval page renders it (see RequestDetailResponse). The one field here the server established itself is DecidedSourceIP above, and that is the approver’s, not the requester’s.

ReportedUsername and ReportedHostname are the “user@host” that asked: the PAM account and machine for a pam or console certificate, the local client’s for a user one. The rest are PAM and console only, and empty on a user certificate, which has no service or terminal to report.

The pair is on a list row too, because it is what a history row leads with: the row’s subject is where the certificate was fetched from, and the approver’s identity is the same value on every row of a person’s own history. So is ReportedService, which a PAM row names (sudo, su, sshd). The remaining six stay detail-only – six more strings on every row is payload nobody reads.

string
retrieved_at

RetrievedAt is when that redemption happened. Distinct from IssuedAt only by the width of the signing round trip, and carried because the retrieval log is timestamped by it — matching a certificate to a line in that log means comparing like with like.

string
retrieved_source_ip

RetrievedSourceIP is the address the service retrieve call came from — the machine the unattended job actually ran on. Present only on a service certificate.

It answers a different question from DecidedSourceIP below, which for a service certificate is the approver’s browser at enrollment time and is therefore identical across every certificate the code mints. This one is this certificate’s own origin.

string
serial_number
required

SerialNumber is a decimal string, not a JSON number. Serials are 63 bits of randomness (internal/serial), so all but a vanishing fraction exceed JavaScript’s Number.MAX_SAFE_INTEGER (2^53-1) and a browser parsing one as a number silently rounds it – 3260700569889958163 reads back as 3260700569889958400. On an audit record that is a wrong answer, and an unsearchable one: the serial an operator reads off ssh-keygen -L never matches what is on screen.

Both tags are needed. json:",string" decides what Go writes; tstype decides what tygo tells the browser to expect, which it otherwise infers from the Go type and gets wrong.

string
type
required

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
error
string

Example

{
"data": {
"serial_number": "0",
"type": "user"
}
}

Invalid certificate ID

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"
}

Certificate not found or not accessible

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"
}