> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sumvin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify a standard-tier Stamped Mandate

> Verify a Sumvin Stamped Mandate JWT end-to-end and print a decoded payload in five minutes.

At the end of this page, you will have run code that validates a <Tooltip headline="Stamped Mandate" tip="A signed authorisation a user grants for specific scoped actions — delivered on the wire as a PINT." cta="Glossary →" href="/glossary">Stamped Mandate</Tooltip> <Tooltip headline="JWT" tip="JSON Web Token — the SIS-issued credential a verifier receives." cta="Glossary →" href="/glossary">JWT</Tooltip> against the <Tooltip headline="SIS" tip="Sumvin Identity Service — the B2B API that exchanges signed PINTs for JWTs." cta="Glossary →" href="/glossary">SIS</Tooltip> <Tooltip headline="JWKS" tip="JSON Web Key Set — the public-key document used to validate PINT JWTs." cta="Glossary →" href="/glossary">JWKS</Tooltip> and prints a decoded payload to stdout — including the `sub`, `wallet`, `kyc_status`, and `scopes` claims — proving your verifier is accepting live Sumvin credentials.

<Snippet file="product-term-disambiguation.mdx" />

A Stamped Mandate is delivered over the API as a `PINT` (header `x-sumvin-pint-token`). This is the **Standard** [Sigil verification tier](/merchant/verification-tiers) — the JWT alone is enough; no signature recovery required.

## Prerequisites

* A Stamped Mandate JWT (the `PINT` from the `x-sumvin-pint-token` header). Either grab one from a test partner, a sample incoming request, or the [verification tiers](/merchant/verification-tiers) reference for a sample token.
* Your registered **audience identifier** (the value SIS puts in the JWT's `aud` claim — provided during partner onboarding).
* A JWT library. This page uses [jose](https://github.com/panva/jose) for TypeScript and [PyJWT](https://pyjwt.readthedocs.io/) for Python.

<Note>
  No SIS API key is required for JWT verification — the JWKS endpoint is public.
</Note>

<Steps>
  <Step title="Install a JWT library">
    <CodeGroup>
      ```bash bun theme={null}
      bun add jose
      ```

      ```bash npm theme={null}
      npm install jose
      ```

      ```bash pip theme={null}
      pip install "pyjwt[crypto]" cryptography
      ```
    </CodeGroup>
  </Step>

  <Step title="Fetch the SIS JWKS">
    The JWKS endpoint is public and holds the ES256 keys SIS uses to sign PINT JWTs. Keys rotate, so use a remote JWK set helper that caches and auto-refreshes rather than hardcoding the payload.

    ```bash theme={null}
    curl https://sis.sumvin.com/v0/sis/.well-known/jwks.json
    ```

    **Response:** `200 OK`

    ```json theme={null}
    {
      "keys": [
        {
          "kty": "EC",
          "crv": "P-256",
          "kid": "sis-001",
          "use": "sig",
          "alg": "ES256",
          "x": "...",
          "y": "..."
        }
      ]
    }
    ```

    <Tip>
      Both `jose.createRemoteJWKSet` and `PyJWKClient` fetch and cache this response for you. You do not need to call this endpoint directly in production code.
    </Tip>
  </Step>

  <Step title="Verify the JWT">
    Validate the signature, issuer, audience, expiry, and algorithm. Anything that fails throws — treat exceptions as verification failures and reject the request.

    <CodeGroup>
      ```typescript TypeScript (jose) theme={null}
      import * as jose from "jose";

      const JWKS_URL = "https://sis.sumvin.com/v0/sis/.well-known/jwks.json";
      const MY_AUDIENCE = "partner-x.example.com";
      const JWKS = jose.createRemoteJWKSet(new URL(JWKS_URL));

      async function verifyPint(token: string) {
        const { payload } = await jose.jwtVerify(token, JWKS, {
          issuer: "https://sis.sumvin.com",
          audience: MY_AUDIENCE,
          algorithms: ["ES256"],
        });
        return payload;
      }

      const payload = await verifyPint(process.env.PINT_JWT!);
      console.log(JSON.stringify(payload, null, 2));
      ```

      ```python Python (PyJWT) theme={null}
      import json
      import os
      import jwt
      from jwt import PyJWKClient

      JWKS_URL = "https://sis.sumvin.com/v0/sis/.well-known/jwks.json"
      MY_AUDIENCE = "partner-x.example.com"

      jwks_client = PyJWKClient(JWKS_URL)

      def verify_pint(token: str) -> dict:
          signing_key = jwks_client.get_signing_key_from_jwt(token)
          return jwt.decode(
              token,
              signing_key.key,
              algorithms=["ES256"],
              audience=MY_AUDIENCE,
              issuer="https://sis.sumvin.com",
          )

      payload = verify_pint(os.environ["PINT_JWT"])
      print(json.dumps(payload, indent=2))
      ```
    </CodeGroup>

    <Warning>
      Pin `algorithms: ["ES256"]`. Accepting anything else — especially `none` or `HS256` — opens you to key-confusion attacks.
    </Warning>
  </Step>

  <Step title="Inspect the decoded payload">
    This is the working artefact. If the Stamped Mandate's JWT is valid, you print a decoded payload that looks like this:

    ```json theme={null}
    {
      "iss": "https://sis.sumvin.com",
      "sub": "sr:us:user:9f8d7e",
      "aud": "partner-x.example.com",
      "jti": "tkn_abc123",
      "iat": 1740000000,
      "exp": 1740003600,
      "wallet": "0xE23c9A70BC749EBddd8c78A864Fd911D04E9e992",
      "kyc_status": "verified",
      "scopes": [
        "sr:us:pint:identity:proof_of_personhood",
        "sr:us:pint:identity:kyc_status"
      ],
      "pint_uri": "sr:us:pint:def456",
      "signer_type": "user",
      "verification_tier": "standard"
    }
    ```

    Claims you act on:

    | Claim               | Meaning                                                                                            | Reference                                          |
    | ------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
    | `sub`               | The user's Sumvin Resource Identifier (SRI). Treat as the stable user ID.                          | [SRI format](/identity/sri)                        |
    | `wallet`            | The user's primary EOA address.                                                                    | [Wallets guide](/guides/wallets)                   |
    | `kyc_status`        | `verified`, `pending`, or `none`. Gate KYC-sensitive features on this.                             | [KYC guide](/guides/kyc)                           |
    | `scopes`            | The SRI capability strings this Stamped Mandate grants. Enforce what you permit against this list. | [Scopes reference](/identity/scopes)               |
    | `pint_uri`          | Stable identifier for the source Stamped Mandate — use this to check revocation.                   | [Revocation checks](/merchant/revocation)          |
    | `verification_tier` | `standard` here, `enhanced` when signatures are attached.                                          | [Verification tiers](/merchant/verification-tiers) |

    <Check>You have a verified Stamped Mandate. The user is who SIS says they are, the scopes in front of you are authorised, and you have their SRI and wallet ready to act on.</Check>
  </Step>

  <Step title="Next: check revocation (optional)">
    A signed JWT proves identity at issuance, but users can revoke a Stamped Mandate before the `exp` time. For high-stakes actions, hit the SIS revocation endpoint with the `pint_uri` from the payload before you act. See [revocation checks](/merchant/revocation) for the full flow.
  </Step>
</Steps>

## What's next

| Next          | Where                                              | When                                                          |
| ------------- | -------------------------------------------------- | ------------------------------------------------------------- |
| JWT deep dive | [Verify a JWT](/merchant/verify-jwt)               | Full reference for signature, audience, and expiry validation |
| Tier picker   | [Verification tiers](/merchant/verification-tiers) | When you need to decide Standard vs Enhanced                  |
| Revocation    | [Revocation checks](/merchant/revocation)          | Before acting on high-stakes requests                         |
| JWKS detail   | [JWKS reference](/merchant/jwks)                   | Key rotation and caching guidance                             |
