Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.getpara.com/llms.txt

Use this file to discover all available pages before exploring further.

Para’s Server SDK can import a session that a user created in your client application. This lets your backend perform authenticated operations for that user, including signing, without asking the user to re-authenticate for the server request. Session validity and duration are enforced by the Para API and the API key’s session length configuration.

Importing Client Sessions

Export the authenticated client session, send it to your backend over HTTPS, then import it into a fresh ParaServer instance for the request.
Create a new ParaServer instance for each imported session. A server SDK instance holds one active session at a time, so reusing one instance across users can mix request state.

Client-Side Session Export

Use waitAndExportSession() after the user authenticates:
const serializedSession = await para.waitAndExportSession();

await fetch("/api/sign-message", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    session: serializedSession,
    message: "Hello from Para",
  }),
});
waitAndExportSession() waits until the SDK has reached an authenticated state before reading session data. Use it for handoff flows immediately after login.
If your backend needs to sign for the user, do not export with { excludeSigners: true }. That option removes the wallet signer data required for server-side signing.
If your backend only needs to validate the user session and will not sign, you can export without signer data:
const sessionWithoutSigners = await para.waitAndExportSession({
  excludeSigners: true,
});

Server-Side Session Import

Import the serialized session before running user-authenticated operations:
import { Para as ParaServer } from "@getpara/server-sdk";
import express from "express";

const app = express();
app.use(express.json());

const paraApiKey = process.env.PARA_API_KEY;
if (!paraApiKey) {
  throw new Error("PARA_API_KEY is required");
}

app.post("/api/sign-message", async (req, res) => {
  const { session, message } = req.body;
  const para = new ParaServer(paraApiKey);

  await para.importSession(session);

  if (!(await para.isSessionActive())) {
    return res.status(401).json({ error: "Session expired" });
  }

  const sessionExtended = await para.keepSessionAlive();
  if (!sessionExtended) {
    return res.status(401).json({ error: "Session expired" });
  }

  const walletId = para.findWalletId();
  const result = await para.signMessage({
    walletId,
    messageBase64: Buffer.from(message).toString("base64"),
  });

  return res.status(200).json({
    walletId,
    signature: result.signature,
  });
});
keepSessionAlive() extends the active imported session according to the API key’s configured session length. It returns false if the session cannot be extended.

Handling Multiple User Sessions

Each exported session belongs to one authenticated user. Import each session into its own ParaServer instance when your backend handles multiple users or parallel requests:
import { Para as ParaServer } from "@getpara/server-sdk";

async function signForImportedSession(session: string, message: string) {
  const para = new ParaServer(process.env.PARA_API_KEY!);
  await para.importSession(session);

  const walletId = para.findWalletId();
  return para.signMessage({
    walletId,
    messageBase64: Buffer.from(message).toString("base64"),
  });
}

const [firstSignature, secondSignature] = await Promise.all([
  signForImportedSession(firstUserSession, "Message for first user"),
  signForImportedSession(secondUserSession, "Message for second user"),
]);

Session Validation

You can validate sessions on the server side to ensure they’re still active before performing operations.

Using the Para Client

import { Para as ParaServer } from "@getpara/server-sdk";

app.post("/api/authenticated-action", async (req, res) => {
  const paraServer = new ParaServer("YOUR_API_KEY");

  await paraServer.importSession(req.body.session);

  if (!(await paraServer.isSessionActive())) {
    return res.status(401).json({ error: "Session expired" });
  }

  return res.status(200).json({ success: true });
});

Using JWT Authentication

Once a user is signed in, you can request a Para JWT token. This token will provide attestations for the user’s ID, their identity, and any wallets they have provisioned via your application. To request a token, use the issueJwt method. The method returns the token itself as well as the JWKS key ID (kid) for the keypair that signed it.
const paraServer  = new ParaServer('your-api-key');

const { token, keyId } = await paraServer.issueJwt();
The token’s expiry will be determined by your customized session length, or else will default to 30 minutes. Issuing a token, like most authenticated API operations, will also renew and extend the session for that duration.
The token’s aud field will be set to your API key’s unique ID, linking it specifically to your application.
Depending on the user in question, a decoded token payload might resemble the following:
{
  "data": {
    "userId": "d5358219-38d3-4650-91a8-e338131d1c5e",
    "wallets": [
      {
        "id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
        "type": "EVM",
        "address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
        "publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
      },
      {
        "id": "d70f64e4-266a-457e-9cea-eeb42341a975",
        "type": "SOLANA",
        "address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
        "publicKey": ""
      }
    ],
    "email": "email@example.com",
    "authType": "email",
    "identifier": "email@example.com",
    "oAuthMethod": "google" // or: undefined | "x" | "discord" | "facebook" | "apple"
  },
  "iat": 1745877709,
  "exp": 1745879509,
  "aud": "a31b8f2e-7c6d-4e5a-9f8b-1d2c3a4b5e6f",
  "sub": "d5358219-38d3-4650-91a8-e338131d1c5e"
}
Para’s JSON Web Keys Set (JWKS) file(s) are available at the following URLs:
EnvironmentJWKS URL
BETAhttps://api.beta.getpara.com/.well-known/jwks.json
PRODhttps://api.getpara.com/.well-known/jwks.json

Using Verification Tokens

For non-Node.js servers or scenarios where you only need to validate a session without importing it, Para provides dedicated verification endpoints:
// Client-side: Get a verification token
const verificationToken = await para.getVerificationToken();

// Send to your server
await fetch("/api/verify-session", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ verificationToken }),
});
On your server, verify the token against Para’s API.
Use your Secret API Key from the Developer Portal to authenticate requests to the verification endpoints. This key is different from the public-facing API Key used in the Para client.
// Server-side verification
app.post("/api/verify-session", async (req, res) => {
  const { verificationToken } = req.body;

  if (!verificationToken) {
    return res.status(400).json({ error: "Missing verification token" });
  }

  // Set the correct URL based on your environment
  const verifyUrl = "https://api.beta.getpara.com/sessions/verify";
  
  const response = await fetch(verifyUrl, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-external-api-key": "YOUR_SECRET_API_KEY"
    },
    body: JSON.stringify({ verificationToken }),
  });

  if (response.status === 403) {
    return res.status(403).json({ error: "Session expired" });
  }

  const userData = await response.json();
  return res.status(200).json({ userData });
});
The verification endpoints are environment-specific:
EnvironmentVerification URL
BETAhttps://api.beta.getpara.com/sessions/verify
PRODhttps://api.getpara.com/sessions/verify
The verification response will contain the authentication type, identifier, and optionally the OAuth method used:
{
  authType: "email" | "phone" | "farcaster" | "telegram" | "externalWallet";
  identifier: string;
  oAuthMethod?: "google" | "x" | "discord" | "facebook" | "apple";
}

Session Management

Maintaining Session Validity

To extend the validity of the session imported into the current ParaServer instance, call keepSessionAlive():
const success = await paraServer.keepSessionAlive();

if (!success) {
  throw new Error("Session expired");
}
Session length is configured per API key in the or CLI. The Para API enforces that duration, and keepSessionAlive() extends the active session according to the configured value.
refreshSession() starts a login or refresh flow and returns a login URL. For imported server sessions, use keepSessionAlive() to extend the active session.

Best Practices

  1. Create a fresh server instance per session: Initialize a new Para Server SDK instance for each imported user session or request.
  2. Secure session transport: Always use HTTPS when transferring sessions between client and server. Do not log serialized sessions.
  3. Export signer data only when needed: Use { excludeSigners: true } only when the server does not need to sign.
  4. Validate before operations: Check that the imported session is active before performing authenticated operations.
  5. Handle expiration explicitly: If the session is expired or cannot be extended, ask the client to authenticate again and export a new session.
  6. Use verification tokens for auth-only checks: When you only need to verify who the user is, use verification tokens instead of importing a full session.
  7. Configure session length intentionally: Set the API key’s session length in the Developer Portal or CLI based on your application’s security model.

Verifying Wallet Ownership

To verify that a wallet address matches one of your users’ embedded wallets, you can send a request to one of the following endpoints:
EnvironmentURL
BETAhttps://api.beta.getpara.com/wallets/verify
PRODhttps://api.getpara.com/wallets/verify
Use your Secret API Key from the Developer Portal to authenticate requests to this endpoint. This key is different from the public-facing API Key used in the Para client.
Pass the address for the wallet in the POST request body:
Node.js
// Server-side verification
app.post("/api/verify-wallet", async (req, res) => {
  const { address } = req.body;
  
  if (!address) {
    return res.status(400).json({ error: "Missing address" });
  }
  
  // Set the correct URL based on your environment
  const verifyUrl = "https://api.beta.getpara.com/wallets/verify";
  
  const response = await fetch(verifyUrl, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-external-api-key": "YOUR_SECRET_API_KEY"
    },
    body: JSON.stringify({ address }),
  });

  if (response.status === 404) {
    return res.status(404).json({ error: `Wallet not found with address: ${address}` });
  }

  const { walletId } = await response.json();
  return res.status(200).json({ walletId });
});

Learn More

For more information about client-side session management and authentication, refer to our web documentation:

Examples

To learn more about using sessions on the server, check out this example. Each example route will have both pregen and session based routes for you to test with.