Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.presschain.io/llms.txt

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

window.presskey API

PressKey exposes a typed request interface on the window object:
// Core request pattern
window.presskey.request({ action: "ACTION_NAME", ...params })

// All confirmed actions:
window.presskey.request({ action: "PING" })
window.presskey.request({ action: "CONNECT" })
window.presskey.request({ action: "GET_WALLET" })
window.presskey.request({ action: "GET_CHAIN_ID" })
window.presskey.request({ action: "SIGN_MESSAGE",   message: "..." })
window.presskey.request({ action: "SIGN_TRANSACTION", tx: {...} })
window.presskey.request({ action: "SUBMIT_CAPSULE",  capsule: {...} })
window.presskey.request({ action: "SUBMIT_SUPPORTING_CAPSULE", capsuleId: "...", data: {...} })
window.presskey.request({ action: "SUBMIT_DISPUTE",  capsuleId: "...", claims: [...] })
window.presskey.request({ action: "SUBMIT_VOTE",     capsuleId: "...", support: true })
window.presskey.request({ action: "GRANT_ROLE",      role: 2, outletSlug: "..." })
window.presskey.request({ action: "VERIFY_OUTLET",   outletSlug: "..." })
window.presskey.request({ action: "LINK_DOMAIN",     domain: "...", outletSlug: "..." })

Nonce Challenge Flow

Every write action follows this path:
App requests action
        |
        v
API Gateway issues nonce (single-use, 30s expiry)
        |
        v
PressKey signs nonce locally (key never leaves device)
        |
        v
PressKey returns { signature, sessionToken, address }
        |
        v
App forwards signed payload to Bridge API
        |
        v
Gateway verifies signature + role status

postMessage Event Matrix

PressKey also communicates via window.postMessage. All messages follow the shape { type: string, payload: object }.

Inbound (App to PressKey)

PRESSKEY_ALLOW_SITERequest site permission to interact with PressKey
PRESSKEY_REQUESTRequest a signed action (vote, publish, role purchase, etc.)
PRESSKEY_FORWARD_RPCForward an RPC call through PressKey’s provider
PRESSKEY_RPC_REQUESTDirect RPC call via PressKey’s injected EIP-1193 provider
PRESSKEY_OPEN_BOND_APPROVALOpen PressKey UI for bond deposit or dissolution approval
PRESSKEY_OPEN_CAPSULE_APPROVALOpen PressKey UI for Capsule publish approval (30 PRESS fee)
PRESSKEY_OPEN_SEND_APPROVALOpen PressKey UI for a PRESS token send approval
PRESSKEY_GET_PENDING_APPROVALQuery the current queue of pending approvals in PressKey
PRESSKEY_APPROVE_PENDING_APPROVALApprove a specific pending action in PressKey
PRESSKEY_REJECT_PENDING_APPROVALReject a specific pending action in PressKey
PRESSKEY_REFRESH_TX_STATUSRequest a refresh of the status for a pending transaction
PRESSKEY_GET_LATEST_TXFetch the most recent transaction from PressKey history

Outbound (PressKey to App)

PRESSKEY_EXTENSIONExtension has loaded and is ready. Payload includes address, roles, balance, and network.
PRESSKEY_PAGEPage-level PressKey state update (navigation, context change)
PRESSGeneral PRESS token event: balance change, transfer confirmed
FETCH_FAILEDA PressKey-proxied fetch request failed. Check payload for error details.
OFFCHAIN_FAULTAn off-chain action failed (Bridge error, API timeout, session invalid)

Integration Example

class PressKeyAdapter {
  private address: string | null = null;
  ready = false;

  constructor() {
    window.addEventListener("message", this.handle.bind(this));
  }

  private handle(e: MessageEvent) {
    const { type, payload } = e.data ?? {};
    if (type === "PRESSKEY_EXTENSION") {
      this.address = payload.address;
      this.ready = true;
    }
    if (type === "OFFCHAIN_FAULT") console.error("PressKey fault:", payload);
    if (type === "FETCH_FAILED")   console.error("PressKey fetch failed:", payload);
  }

  async connect(): Promise<string> {
    const r = await window.presskey.request({ action: "CONNECT" });
    this.address = r.address;
    return r.address;
  }

  async vote(capsuleId: string, support: boolean) {
    const { signature, sessionToken } = await window.presskey.request({
      action: "SUBMIT_VOTE",
      capsuleId,
      support,
    });

    return fetch(`https://bridge.presschain.io/capsules/${capsuleId}/accept`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${sessionToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ capsuleId, support, signature }),
    });
  }

  async submitCapsule(data: object) {
    const { signature, sessionToken } = await window.presskey.request({
      action: "SUBMIT_CAPSULE",
      capsule: data,
    });

    return fetch("https://bridge.presschain.io/capsules/create", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${sessionToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ ...data, signature }),
    });
  }
}

// Usage
const pk = new PressKeyAdapter();
await pk.connect();
await pk.vote("247", true);
Standard React state for PressKey-driven modals in portal apps:
const [showCreateCapsuleModal, setShowCreateCapsuleModal] = useState(false);
const [showRightsModal,        setShowRightsModal]        = useState(false);
const [showDisputeModal,       setShowDisputeModal]       = useState(false);
const [showRoleModal,          setShowRoleModal]          = useState(false);

const [wallet,          setWallet]          = useState(null);
const [walletConnected, setWalletConnected] = useState(false);
const [latestTx,        setLatestTx]        = useState(null);
const [chainId,         setChainId]         = useState(77117002);
const [networkName,     setNetworkName]     = useState("PressChain Testnet");

WordPress Security Boundary

WordPress never handles keys. The signing boundary is strictly:
WordPress PHP/JS
      |
      |  POST signed payload only — never raw keys, never RPC
      v
API Gateway (rate limiting, nonce verify, replay protection)
      |
      v
Bridge API -> Protocol Contracts
WordPress sees a web API. This is a hard architectural rule, not a best practice.