Fix revocation docs: use POST /v1/validate, not a phantom license-status endpoint

GET /v1/licenses/{id}/status does not exist. Revocation is checked via
POST /v1/validate, which returns ok:false / reason:"revoked".
This commit is contained in:
Keysat
2026-06-13 06:40:08 -05:00
parent 87fd4f32e3
commit 23681bc05e
+22 -14
View File
@@ -198,32 +198,40 @@ result = verifier.<span class="f">verify</span>(license_key_from_user)
<h2 id="renewals">Renewals &amp; revocation</h2>
<p>Keysat licenses are signed at issue time and do not phone home. If a license is revoked in the admin UI, the existing key continues to verify in your app. That&rsquo;s the trade-off for offline.</p>
<p>If you need revocation, ship a thin <em>online</em> check that runs on a cadence (e.g. once a week) against your Keysat&rsquo;s revocation feed:</p>
<p>If you need revocation, ship a thin <em>online</em> check that re-validates the key on a cadence (e.g. once a week) against your Keysat&rsquo;s <code>POST /v1/validate</code>. A revoked license returns <code>ok: false</code> with <code>reason: "revoked"</code>:</p>
<pre class="code lang-pane" data-lang="ts"><span class="c">// Optional. Run on a cadence, ignore network errors.</span>
<span class="k">async function</span> <span class="f">checkRevocation</span>(licenseId: string) {
<span class="k">const</span> r = <span class="k">await</span> fetch(<span class="s">`https://your-keysat.example/v1/licenses/${licenseId}/status`</span>);
<span class="k">async function</span> <span class="f">checkRevocation</span>(licenseKey: string) {
<span class="k">const</span> r = <span class="k">await</span> fetch(<span class="s">'https://your-keysat.example/v1/validate'</span>, {
method: <span class="s">'POST'</span>,
headers: { <span class="s">'Content-Type'</span>: <span class="s">'application/json'</span> },
body: JSON.<span class="f">stringify</span>({ key: licenseKey }),
});
<span class="k">if</span> (r.ok) {
<span class="k">const</span> j = <span class="k">await</span> r.json();
<span class="k">if</span> (j.status === <span class="s">'revoked'</span>) <span class="f">disableApp</span>();
<span class="k">if</span> (!j.ok &amp;&amp; j.reason === <span class="s">'revoked'</span>) <span class="f">disableApp</span>();
}
}</pre>
<pre class="code lang-pane" data-lang="rs" style="display:none"><span class="c">// Optional. Run on a cadence, ignore network errors.</span>
<span class="k">async fn</span> check_revocation(license_id: &amp;<span class="k">str</span>) {
<span class="k">if let</span> <span class="k">Ok</span>(r) = reqwest::get(format!(
<span class="s">"https://your-keysat.example/v1/licenses/{}/status"</span>,
license_id
)).<span class="k">await</span> {
<span class="k">if let</span> <span class="k">Ok</span>(j) = r.json::&lt;Status&gt;().<span class="k">await</span> {
<span class="k">if</span> j.status == <span class="s">"revoked"</span> { disable_app(); }
<span class="k">async fn</span> check_revocation(license_key: &amp;<span class="k">str</span>) {
<span class="k">let</span> body = serde_json::json!({ <span class="s">"key"</span>: license_key });
<span class="k">if let</span> <span class="k">Ok</span>(r) = reqwest::<span class="f">Client</span>::new()
.post(<span class="s">"https://your-keysat.example/v1/validate"</span>)
.json(&amp;body)
.send()
.<span class="k">await</span>
{
<span class="k">if let</span> <span class="k">Ok</span>(j) = r.json::&lt;ValidateResp&gt;().<span class="k">await</span> {
<span class="k">if</span> !j.ok &amp;&amp; j.reason.as_deref() == <span class="k">Some</span>(<span class="s">"revoked"</span>) { disable_app(); }
}
}
}</pre>
<pre class="code lang-pane" data-lang="py" style="display:none"><span class="c"># Optional. Run on a cadence, ignore network errors.</span>
<span class="k">def</span> <span class="f">check_revocation</span>(license_id):
<span class="k">def</span> <span class="f">check_revocation</span>(license_key):
<span class="k">try</span>:
r = requests.get(<span class="s">f"https://your-keysat.example/v1/licenses/{license_id}/status"</span>, timeout=<span class="n">5</span>)
<span class="k">if</span> r.json()[<span class="s">"status"</span>] == <span class="s">"revoked"</span>:
r = requests.post(<span class="s">"https://your-keysat.example/v1/validate"</span>, json={<span class="s">"key"</span>: license_key}, timeout=<span class="n">5</span>)
j = r.json()
<span class="k">if</span> <span class="k">not</span> j[<span class="s">"ok"</span>] <span class="k">and</span> j.get(<span class="s">"reason"</span>) == <span class="s">"revoked"</span>:
disable_app()
<span class="k">except</span> Exception:
<span class="k">pass</span></pre>