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

# Screen Wallet PDF

> Stateless companion to `POST /v3/wallet-screening/`: repost the exact JSON body that endpoint returned — including the `report_signature` it added — and get back a Didit-branded PDF of the same result. No provider is called and nothing is billed or persisted; this only re-renders a result Didit already produced. The signature ties the PDF to an unmodified result: editing `risk_score`, `severity`, `sanctions_hit`, or any other signed field before reposting fails with `400`.

export const AgentPromptAccordion = ({prompt, title = "AI Agent Integration Prompt"}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = e => {
    e.stopPropagation();
    if (!prompt) return;
    navigator.clipboard.writeText(prompt.trim()).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const agents = ["Claude Code", "Codex", "Cursor", "Devin", "Windsurf", "GitHub Copilot"];
  return <div className="didit-agent-card">
      {}
      <div className="didit-agent-titlebar">
        <div className="didit-agent-dots" aria-hidden="true">
          <span className="didit-agent-dot didit-agent-dot-red"></span>
          <span className="didit-agent-dot didit-agent-dot-yellow"></span>
          <span className="didit-agent-dot didit-agent-dot-green"></span>
        </div>
        <span className="didit-agent-filename">{title}</span>
        <button type="button" className={`didit-agent-copy ${copied ? "didit-agent-copy-copied" : ""}`} onClick={handleCopy} title="Copy prompt to clipboard" aria-label={copied ? "Copied!" : "Copy prompt to clipboard"}>
          {copied ? <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <path d="M3 8.5l3.5 3.5L13 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              <span>Copied</span>
            </> : <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1.5" />
                <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5" stroke="currentColor" strokeWidth="1.5" />
              </svg>
              <span>Copy</span>
            </>}
        </button>
      </div>

      {}
      <pre className="didit-agent-body"><code>{prompt.trim()}</code></pre>

      {}
      <div className="didit-agent-footer">
        <span className="didit-agent-footer-label">Paste into</span>
        <div className="didit-agent-chips">
          {agents.map(name => <span key={name} className="didit-agent-chip">{name}</span>)}
        </div>
      </div>
    </div>;
};

<AgentPromptAccordion
  title="Screen Wallet PDF Prompt"
  prompt={`Render a previously returned on-demand wallet-screening result as a Didit-branded PDF, through the Didit Management API.

Endpoint:
POST https://verification.didit.me/v3/wallet-screening/pdf/

Authentication:
Use the x-api-key header with my Didit API key.

Goal:
- Turn a wallet-screening JSON result into a downloadable PDF report. This is stateless: nothing is persisted, no provider is called again, and there is no additional billing.

Prerequisites:
- You must already have the exact JSON body that POST /v3/wallet-screening/ returned, including its report_signature field. This endpoint does not accept a hand-built or edited payload.

Request body (application/json):
- The full, unmodified response body from POST /v3/wallet-screening/ (or its console equivalent), including report_signature.

Example call:
curl -X POST "https://verification.didit.me/v3/wallet-screening/pdf/" \\
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \\
-d "$RESULT" \\
-o wallet_screening_report.pdf

Response:
- Body is binary (application/pdf). DO NOT parse as JSON.
- Content-Type: application/pdf
- Content-Disposition: attachment; filename=wallet_screening_<address>.pdf

Failure modes:
- 400 - the body is not a JSON object matching the wallet-screening result shape, or report_signature is missing or invalid (the result was edited, truncated, or hand-built instead of reposted verbatim).
- 401 - missing or invalid x-api-key.

For the underlying screening call, see /management-api/transactions/screen-wallet.`}
/>

## Why it's stateless

Standalone wallet screening writes nothing to the transactions table, so there is no stored record for this endpoint to render from. Instead, you repost the exact JSON that `POST /v3/wallet-screening/` already gave you, and Didit renders that payload into a PDF without calling a provider again.

## The `report_signature` field

Every `POST /v3/wallet-screening/` response now includes a `report_signature` field — an opaque signature scoped to your application. This endpoint verifies it before rendering, so a caller can't edit `risk_score`, `severity`, `sanctions_hit`, or any other field and get a clean-looking Didit-branded PDF for a result Didit never produced.

Always repost the full response body **unmodified**, including `report_signature`. Reordering keys is fine; changing any signed value is not.

```bash theme={null}
# 1. Run the screening and save the full response
RESULT=$(curl -s -X POST https://verification.didit.me/v3/wallet-screening/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"wallet_address": "0x28c6c06298d514db089934071355e5743bf21d60", "blockchain": "ETH"}')

# 2. Repost it verbatim to render the PDF
curl -X POST https://verification.didit.me/v3/wallet-screening/pdf/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$RESULT" \
  -o wallet_screening_report.pdf
```

## Billing

No provider call is made and nothing is billed — this only re-renders a result you already paid for when you called `POST /v3/wallet-screening/`.

## Errors

| Status | Meaning                                                                                                                                       |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Body is not a JSON object matching the wallet-screening result shape, or `report_signature` is missing or does not match the reposted fields. |
| `401`  | Missing or invalid `x-api-key`.                                                                                                               |

## Next steps

<CardGroup cols={2}>
  <Card title="Screen Wallet" icon="code" href="/management-api/transactions/screen-wallet">
    Run the underlying on-demand wallet screening and get the JSON result to repost here.
  </Card>

  <Card title="On-demand wallet screening" icon="link" href="/transaction-monitoring/wallet-screening">
    Overview of the standalone wallet-screening endpoint and result model.
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /v3/wallet-screening/pdf/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/wallet-screening/pdf/:
    post:
      tags:
        - Transactions
      summary: Render a wallet-screening result as a PDF
      description: >-
        Stateless companion to `POST /v3/wallet-screening/`: repost the exact
        JSON body that endpoint returned — including the `report_signature` it
        added — and get back a Didit-branded PDF of the same result. No provider
        is called and nothing is billed or persisted; this only re-renders a
        result Didit already produced. The signature ties the PDF to an
        unmodified result: editing `risk_score`, `severity`, `sanctions_hit`, or
        any other signed field before reposting fails with `400`.
      operationId: screenWalletPdf
      requestBody:
        required: true
        description: >-
          The unmodified JSON object returned by `POST /v3/wallet-screening/`,
          including `report_signature`.
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/WalletScreeningResult'
              required:
                - report_signature
              description: >-
                Same shape as the `POST /v3/wallet-screening/` response — pass
                that response body back verbatim, including `report_signature`.
      responses:
        '200':
          description: >-
            The rendered PDF document, returned directly as binary
            `application/pdf` — there is no JSON wrapper.
          headers:
            Content-Disposition:
              description: >-
                `attachment; filename=wallet_screening_{address}.pdf`, derived
                from the `wallet_address` (or `transaction_hash`) in the
                reposted result.
              schema:
                type: string
                example: >-
                  attachment;
                  filename=wallet_screening_0x28c6c06298d514db0899.pdf
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '400':
          description: >-
            The body is not a JSON object matching the wallet-screening result
            shape, or `report_signature` is missing or does not match the
            reposted fields (edited or truncated before repost).
          content:
            application/json:
              schema:
                type: object
              examples:
                Not an object:
                  value:
                    non_field_errors:
                      - >-
                        Expected a JSON object matching a wallet-screening
                        result.
                Missing or invalid signature:
                  value:
                    report_signature:
                      - >-
                        This wallet-screening result is missing or fails its
                        report signature - repost the exact JSON returned by the
                        wallet-screening endpoint, unmodified.
        '401':
          description: Missing or invalid `x-api-key`.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: >-
            # $RESULT is the saved JSON body from POST /v3/wallet-screening/

            curl -X POST
            'https://verification.didit.me/v3/wallet-screening/pdf/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d "$RESULT" \
              -o wallet_screening_report.pdf
        - lang: python
          label: Python
          source: >-
            import requests


            # `result` is the dict returned by POST /v3/wallet-screening/,
            unmodified

            response = requests.post(
                'https://verification.didit.me/v3/wallet-screening/pdf/',
                headers={'x-api-key': 'YOUR_API_KEY'},
                json=result,
                timeout=30,
            )

            response.raise_for_status()

            with open('wallet_screening_report.pdf', 'wb') as fh:
                fh.write(response.content)
        - lang: javascript
          label: JavaScript
          source: >-
            // `result` is the object returned by POST /v3/wallet-screening/,
            unmodified

            const response = await
            fetch('https://verification.didit.me/v3/wallet-screening/pdf/', {
              method: 'POST',
              headers: {
                'x-api-key': process.env.DIDIT_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify(result),
            });

            if (!response.ok) throw new Error(`PDF generation failed: HTTP
            ${response.status}`);

            const pdfBuffer = Buffer.from(await response.arrayBuffer());
components:
  schemas:
    WalletScreeningResult:
      type: object
      properties:
        provider:
          type: string
          description: >-
            Provider that performed the screening (e.g. `merklescience`,
            `crystal`).
        screening_type:
          type: string
          enum:
            - WALLET_SCREENING
          description: Always `WALLET_SCREENING` for this endpoint.
        risk_score:
          type: integer
          description: >-
            Normalised 0-100 risk score. Higher means greater exposure to risky
            entities.
        severity:
          type: string
          enum:
            - UNKNOWN
            - LOW
            - MEDIUM
            - HIGH
            - CRITICAL
          description: >-
            Risk bucket derived from `risk_score`: `0-9` UNKNOWN, `10-39` LOW,
            `40-69` MEDIUM, `70-89` HIGH, `90-100` CRITICAL. `UNKNOWN` is the
            lowest band, not a separate no-data state: `risk_score` 0 (the
            common clean-address case) means no adverse assessment, while a
            non-zero score in the 1-9 range is a real but sub-LOW signal - read
            the `risk_score`, not just the band. Never treat `UNKNOWN` as an
            affirmative low-risk or clear rating; do not display it as a pass.
        status:
          type: string
          enum:
            - SCREENED
            - PENDING
            - ERROR
          description: Screening outcome status.
        summary:
          type: string
          description: Human-readable summary of the screening result.
        wallet_address:
          type: string
          description: The screened address, echoed back.
        blockchain:
          type: string
          description: The blockchain that was screened.
        sanctions_hit:
          type: boolean
          description: True if the address has direct or indirect sanctions exposure.
        dominant_risk_category:
          type: string
          nullable: true
          description: >-
            Highest-weighted high-risk category, or `null` when none is dominant
            (e.g. `sanctioned`, `mixer`, `stolen_funds`).
        source_of_funds:
          type: array
          description: >-
            Where the address received funds from, attributed by entity. Each
            entry is an exposure breakdown.
          items:
            type: object
            properties:
              category:
                type: string
                description: >-
                  Normalised risk category, e.g. `exchange_licensed`, `dex`,
                  `defi`, `payment_processor`, `mixer`, `darknet_market`,
                  `sanctioned`, `stolen_funds`, `high_risk_exchange`.
              entity_name:
                type: string
                description: >-
                  Name of the attributed entity (e.g. `Binance.com`, `Tornado
                  Cash`).
              entity_type:
                type: string
                description: >-
                  Provider entity type (e.g. `Exchange`, `Mixer`, `DeFi`,
                  `Sanctions`, `Theft`).
              entity_subtype:
                type: string
                description: >-
                  More granular subtype (e.g. `Mandatory KYC and AML`, `OFAC
                  SDN`, `Yield Aggregator`).
              exposure_direction:
                type: string
                enum:
                  - incoming
                  - outgoing
                  - connected
                description: >-
                  Whether the funds came from (`incoming`) or went to
                  (`outgoing`) this entity.
              exposure_type:
                type: string
                enum:
                  - direct
                  - indirect
                description: '`direct` (1 hop) or `indirect` (multi-hop) exposure.'
              is_direct:
                type: boolean
                description: True when the exposure is direct (0 hops).
              amount_usd:
                type: number
                description: USD value attributed to this entity.
              percentage:
                type: number
                description: Share of the direction's total exposure, 0-100.
              hops:
                type: integer
                description: Number of hops to the entity (0 for direct exposure).
              country:
                type: string
                description: Primary ISO 3166-1 alpha-2 country of the entity, when known.
        destination_of_funds:
          type: array
          description: >-
            Where the address sent funds to, attributed by entity. Same item
            shape as `source_of_funds` with `exposure_direction` = `outgoing`.
          items:
            type: object
            properties:
              category:
                type: string
              entity_name:
                type: string
              entity_type:
                type: string
              entity_subtype:
                type: string
              exposure_direction:
                type: string
                enum:
                  - incoming
                  - outgoing
                  - connected
              exposure_type:
                type: string
                enum:
                  - direct
                  - indirect
              is_direct:
                type: boolean
              amount_usd:
                type: number
              percentage:
                type: number
              hops:
                type: integer
              country:
                type: string
        counterparty_connections:
          type: array
          description: >-
            Direct and indirect counterparty entities with received/sent amounts
            and risk levels.
          items:
            type: object
            properties:
              entity_name:
                type: string
                description: Counterparty entity name.
              entity_type:
                type: string
                description: Counterparty entity type.
              entity_subtype:
                type: string
                description: Counterparty entity subtype.
              risk_level:
                type: string
                enum:
                  - UNKNOWN
                  - LOW
                  - MEDIUM
                  - HIGH
                  - CRITICAL
              categories:
                type: array
                items:
                  type: string
                description: Risk categories attributed to the counterparty.
              received_usd:
                type: number
                description: USD received from the counterparty.
              sent_usd:
                type: number
                description: USD sent to the counterparty.
              received_hops:
                type: integer
              sent_hops:
                type: integer
              percentage:
                type: number
              is_direct:
                type: boolean
              country:
                type: string
        report_signature:
          type: string
          description: >-
            Opaque HMAC-SHA256 signature over this exact result, scoped to your
            application. Not meaningful on its own — repost the full response
            body, unmodified and including this field, to `POST
            /v3/wallet-screening/pdf/` to render it as a PDF. The PDF endpoint
            rejects the request with `400` if `report_signature` is missing or
            if any signed field (e.g. `risk_score`, `severity`, `sanctions_hit`)
            was edited before repost.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````