# Sign your first document

The signing iframe lets a customer approve a document without leaving your page. The backend computes the document hash and requests a signing token; the page opens the iframe and receives the signature after passkey approval.

These examples use production. To use the sandbox, register the application there separately and use `https://signa.dev.securysign.com` for both the API and the frame.

## 1. Register your application

Register the application as a relying party (RP), with its name, HTTPS origin, callback URL and contact email:

```bash
curl --fail-with-body -X POST https://securysign.com/api/rp/register \
  -H "Content-Type: application/json" \
  --data '{
    "name": "clientX",
    "origin": "https://app.example.com",
    "redirect_uris": ["https://app.example.com/auth/callback"],
    "contact_email": "dev@example.com",
    "requested_scopes": ["signa:sign"]
  }'
```

Registration returns `{"rpId": 42, "status": "pending"}`. Once approved, the registration provides:

- your client ID, such as `signa-rp-42`, assigned from the registration ID;
- your **Secure Signature Confirmation (SSC) secret**, available on the [RP dashboard](https://cloud.securysign.com/#/rp/dashboard);
- approval for your registered origin to embed the signing frame.

See the [RP integration guide](#/docs/rp-integration-guide) for the full registration request and approval errors.

## 2. Request a signing token

Set `RP_CLIENT_ID` and `SSC_SECRET` in the backend environment using the values from the approved registration. The example below requires Python 3, curl and `sha256sum`, with `Contract.pdf` in the working directory. It hashes the file and builds a JSON body containing the credentials.

```bash
export DOCUMENT_HASH="$(sha256sum Contract.pdf | cut -d ' ' -f 1)"
python3 - <<'JSON' > signing-token-request.json
import json, os
print(json.dumps({
    "clientId": os.environ["RP_CLIENT_ID"],
    "clientSecret": os.environ["SSC_SECRET"],
    "documentHash": os.environ["DOCUMENT_HASH"],
    "loa": "LOA-2"
}))
JSON
curl --fail-with-body -X POST https://securysign.com/api/ssc/token \
  -H "Content-Type: application/json" \
  --data-binary @signing-token-request.json -o signing-token-response.json
rm signing-token-request.json
```

A successful request returns the following fields. The token and expiry shown here are illustrative:

```json
{ "token": "eyJhbGciOi…", "expiresAt": 1790000300, "loa": "LOA-2", "credentialBound": false }
```

Pass `token` and `documentHash` from the backend to the page. The token is valid for five minutes and only for the submitted hash. A `403` response identifies the reason it was rejected: a pending registration, an incorrect SSC secret or an assurance level above the RP's approved maximum. See [Iframe troubleshooting](#/docs/iframe#troubleshooting) for the corresponding fixes. After a `429`, wait for the signing allowance to reset.

## 3. Embed the iframe

Call `openSigning` with the hash and token returned by the backend. `URLSearchParams` handles encoding for the document name and the page's origin:

```html
<iframe id="signa-frame" title="SecurySign signing" width="100%" height="420"
  allow="publickey-credentials-get *"
  sandbox="allow-scripts allow-same-origin allow-forms allow-popups"></iframe>
<script>
  const frame = document.getElementById("signa-frame");
  function openSigning({ documentHash, token }) {
    const params = new URLSearchParams({
      documentHash, token, documentName: "Contract.pdf",
      mode: "registered", rpOrigin: location.origin
    });
    frame.src = `https://securysign.com/#/sign-frame?${params}`;
  }
</script>
```

Serve the page from the RP's approved origin. The `/#/sign-frame` route opens the signing screen; the `allow` attribute permits a passkey prompt inside the cross-origin frame.

## 4. Listen for the result

Check both the sender's origin and its window before handling a message. The functions `saveSignature`, `showError` and `cancel` below stand for the application's own handlers:

```js
window.addEventListener("message", (event) => {
  if (event.origin !== "https://securysign.com" || event.source !== frame.contentWindow) return;
  const msg = event.data;
  if (msg.type === "SSC_SIGN_COMPLETE") saveSignature(msg);
  if (msg.type === "SSC_SIGN_ERROR") showError(msg.error);
  if (msg.type === "SSC_CLOSE_FRAME") cancel();
});
```

## 5. Sign a document

Selecting **Sign** opens the customer's passkey prompt. After approval, the frame sends `SSC_SIGN_COMPLETE`, including `signatureBase64`, `documentHash` and `levelOfAssurance`. Check that the returned hash matches the saved document hash, then store the complete result with the document.

An `RP origin not authorized` error means the embedding origin needs approval. Submit it through [Signing origins](#/docs/rp-integration-guide#3-authorise-additional-signing-origins).

## Next steps

For resizing, batch signing, all frame events and level of assurance 4 (LOA-4), see the [Iframe guide](#/docs/iframe). The [Hash signing API](#/docs/api-hash-signing) covers requests created on the backend, including approval and completion. [KYC](#/docs/kyc) covers identity verification before signing, and the [OpenAPI specification](#/docs/api-reference#get-the-openapi-spec) contains the complete request and response schemas.
