# Integration Guide \[Step-by-step guide for integrating Newton Verifiable Credentials into your application.]

This guide walks through the end-to-end integration of Newton Verifiable Credentials into your app. Before starting, read the [overview](/developers/verified-credential/overview) to understand how Newton VC works.

<Cards>
  <Card icon="github" to="https://github.com/newt-foundation/verifiable-credential-example" title="Example Repo">
    Clone the repo and follow the README to run the full VC flow locally
  </Card>

  <Card icon="browser" to="https://verifiable-credential-example.vercel.app/" title="Live Demo">
    Try the live demo
  </Card>
</Cards>

## Prerequisites

| Requirement | Details |
|-------------|---------|
| **Newton SDK** | `@newton-xyz/sdk` installed in your JavaScript/TypeScript app |
| **Newton API key** | [Create one in the dashboard](/developers/overview/dashboard-api-keys) |
| **Policy client contract** | Must inherit `NewtonPolicyClient` and be registered with `PolicyClientRegistry` |
| **Identity domain** | A fixed `bytes32` hex value for your application: the output of `keccak256("kyc")` |
| **KYC vendor** | A third-party vendor to collect user identity data. The vendor's output must map to Newton's [IdentityData fields](/developers/verified-credential/identity-policy-reference#kyc-data-fields). |

## Step 1: Write and Deploy a Policy

### 1a. Write your Rego policy

Write a Rego policy that uses Newton identity built-in functions. For example, to enforce that a user is approved and at least 18 years old:

```rego
package example

import future.keywords.if

authorized if {
    newton.identity.kyc.check_approved()
    newton.identity.kyc.age_gte(18)
    newton.identity.kyc.address_in_countries(["US","CA","DE"])
}
```

See the [Identity Policy Reference](/developers/verified-credential/identity-policy-reference) for all available built-in functions.

### 1b. Write your `params_schema.json`

Define the schema for your policy params, including the `identity_domain`:

```json
{
  "type": "object",
  "description": "params including domain",
  "required": [],
  "properties": {
    "identity_domain": {
      "type": "string",
      "description": "the identity domain, must be hex"
    }
  }
}
```

### 1c. Write your `policy_metadata.json`

```json
{
  "name": "Newton Verifiable Credential Policy",
  "version": "1.0.0",
  "author": "Newton",
  "link": "https://newton.xyz",
  "description": "This policy enforces KYC compliance"
}
```

:::note
Verifiable Credential policies do not use a `secrets_schema.json` or `policy_data_metadata.json`. You only need `policy_metadata.json` and `params_schema.json`.
:::

### 1d. Deploy your policy using newton-cli

Your policy directory should look like this before deploying:

```
policy_files/
├── params_schema.json
├── policy.rego
└── policy_metadata.json
```

Generate your `policy_cids.json` file with the Newton CLI command documented [here](/developers/reference/command-line-tool#upload-cids).

**Deploy the policy contract**

Once you have `policy_cids.json`, run:

```bash
newton-cli policy deploy --policy-cids policy_cids.json
```

After deploying, note your policy contract address. You need it in Step 2.

## Step 2: Deploy a Policy Client Contract

Your policy client inherits `NewtonPolicyClient`, which implements the EIP-712 domain itself, so don't add OpenZeppelin's `EIP712`. See the [Reference](/developers/verified-credential/reference#sample-policy-client) for a complete sample contract.

Key requirements:

* Inherit `NewtonPolicyClient`, which provides the EIP-712 domain
* Call `_initNewtonPolicyClient(policyTaskManager, owner)` in the constructor
* Register the contract with the `PolicyClientRegistry` (see [Smart Contract Integration](/developers/guides/smart-contract-integration#identityregistry-integration))

**After registering**, call the following on your deployed policy client contract:

Call owner `setPolicies` with the identity policy address and its configuration. Encode the following JSON into that entry's `policyParams`, and set a nonzero `expireAfter`. Include every other desired policy in the same ordered replacement. See the [configuration example](/developers/guides/smart-contract-integration#configure-the-policy-set):

```json
{
  "identity_domain": "0xa29cad97d4110e8822dedef32879f84a8e8889bf965ff9c57998689e2fdafbc4"
}
```

## Step 3: Collect KYC Data

Collect identity data from your users using a third-party KYC vendor. The data you receive from the vendor must map to Newton's [IdentityData fields](/developers/verified-credential/identity-policy-reference#kyc-data-fields), such as `status`, `birthdate`, and `address_country_code`.

## Step 4: Register and Confirm KYC Data

Before invoking the identity popup, your application must display a notice to the user explaining that their identity data will be encrypted and processed by the Newton Protocol. At minimum this notice should state: "Your identity data is encrypted before leaving your device. It is processed by the Newton Protocol to verify your eligibility." You are responsible for ensuring this disclosure is presented, not buried in your terms of service.

### 4a. Register KYC Data

Once you have the KYC data from your vendor, call `registerUserData` to register it with Newton, scoped to your identity domain:

```typescript
const walletClient = createWalletClient({ ... }).extend(
  newtonWalletClientActions({ apiKey: 'YOUR_API_KEY' })
)

await walletClient.registerUserData({
  userData: kycDataFromVendor,
  appIdentityDomain: '0xa29cad97d4110e8822dedef32879f84a8e8889bf965ff9c57998689e2fdafbc4',
  policyClient: '0xYourPolicyClientAddress',
})
```

:::note
When the Newton identity popup opens, Newton creates a new wallet for the user to manage their identity. The user will need to fund this wallet with ETH to continue.
:::

### 4b. User Confirms KYC Data

The user sees a confirmation prompt in the Newton identity popup to verify the KYC data being registered. The popup opened in step 4a handles this automatically, so no additional SDK calls are required.

## Step 5: Link Identity to Your App

### 5a. Initiate Identity Link

Call `linkApp` to ask the user to link their registered identity to your policy client contract:

```typescript
const link = await walletClient.linkApp({
  appWalletAddress: userWalletAddress,
  appClientAddress: yourPolicyClientAddress,
  appIdentityDomain: '0xa29cad97d4110e8822dedef32879f84a8e8889bf965ff9c57998689e2fdafbc4',
})
```

Once you submit it in step 5b, the link associates the user's Newton identity wallet with your policy client contract in the `IdentityRegistry`. After this link is established, Newton's policy engine can access the user's registered KYC data when evaluating tasks submitted through your policy client.

A user's registered identity data can be reused across any application sharing the same identity domain, not just yours. Before calling `linkApp`, surface this to the user in plain language: linking grants your app access to the same KYC data other apps may already have, and other apps may gain access to data registered here.

You can find the `IdentityRegistry` contract address in [Contract Addresses](/developers/reference/contract-addresses).

### 5b. User Confirms the Link

The user confirms the link in the Newton identity popup by signing it with their Newton identity wallet. The popup sends no transaction: `linkApp` resolves with the signed values, and your app submits them by calling `linkIdentityAsUser` on the `IdentityRegistry` from the user's app wallet (`appWalletAddress`). The registry records `msg.sender` as the linked client user, so a transaction from any other account reverts with `InvalidSignature()`. Pass the values through unchanged, because the signature covers them.

```typescript
await walletClient.linkIdentityAsUser({
  identityOwner: link.identityOwner,
  policyClient: yourPolicyClientAddress,
  identityDomains: link.identityDomains,
  identityOwnerSignature: link.signature,
  identityOwnerNonce: BigInt(link.nonce),
  identityOwnerDeadline: BigInt(link.deadline),
})
```

The call sends this `IdentityRegistry` function:

```solidity
function linkIdentityAsUser(
    address _identityOwner,      // user's Newton identity wallet
    address _policyClient,       // your policy client contract
    bytes32[] _identityDomains,  // [your identity domain]
    bytes _signature,            // identity owner's signature over the link
    uint256 _nonce,
    uint256 _deadline
)
```

See [SDK & Contract Reference](/developers/verified-credential/reference#linkidentityasuser) for the full function signature and parameters.

## Step 6: Submit a Task

Submit a task with a signed intent from the user's dApp wallet. The intent must be constructed and signed using the EIP712 domain of your policy client.

**1. Construct the intent**

```typescript
const intent = {
  from: userAddress,
  to: yourPolicyClientAddress,   // the contract that will execute the tx
  value: '0x0' as Hex,           // ETH value as hex string
  data: '0x...' as Hex,          // encoded calldata for the target call
  chainId: 11155111,             // your chain ID
  functionSignature: '0x' as Hex,
}
```

**2. Sign the intent using your policy client's EIP712 domain**

```typescript
// NewtonPolicyClient fixes the domain name and version; eip712Domain() on your policy client returns them
const intentSignature = await walletClient.signTypedData({
  account: userAddress,
  domain: {
    name: 'NewtonPolicyClient',  // fixed by the NewtonPolicyClient mixin
    version: '1',
    chainId: 11155111,
    verifyingContract: yourPolicyClientAddress,
  },
  types: {
    Intent: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'data', type: 'bytes' },
      { name: 'chainId', type: 'uint256' },
      { name: 'functionSignature', type: 'bytes' },
    ],
  },
  primaryType: 'Intent',
  message: {
    ...intent,
    value: BigInt(intent.value),
    chainId: BigInt(intent.chainId),
  },
})
```

**3. Submit for evaluation**

```typescript
const { result } = await walletClient.evaluateIntentDirect({
  policyClient: yourPolicyClientAddress,
  intent,
  intentSignature,
  timeout: 60,
  wasmArgs: ['0x'], // One pure-Rego identity policy; align with your actual set
})
// result.allowed: true if the policy passed
// result.task, result.taskResponse, result.blsSignature: needed for Step 7
```

:::warning
Identity policies need `intentSignature`. Operators recover the signer under your policy client's EIP-712 domain and load the identity data of every domain linked to the policy client for that signer. If the signature is missing, operators skip identity data without an error and the identity policy denies. If it is present but does not recover to `intent.from`, the gateway rejects the task and `evaluateIntentDirect` throws a `GatewayError` (`intent_signature does not verify: ...`). The task request takes no identity domain: SDK 1.3.1 and earlier send `identityDomain`, and the gateway ignores it.
:::

## Step 7: Execute Onchain Transaction with Attestation

Use the attestation returned in Step 6 to execute the transaction onchain through your policy client.

```typescript
if (result.allowed !== true) throw new Error("Policy set denied the intent");
const { task, taskResponse, blsSignature } = result

await walletClient.writeContract({
  address: yourPolicyClientAddress,
  abi: policyClientAbi,
  functionName: 'validateAndExecuteDirect',
  args: [
    intent.to,
    BigInt(intent.value),
    intent.data,
    {
      ...task,
      intent: { ...task.intent, value: BigInt(task.intent.value), chainId: BigInt(task.intent.chainId) },
      initializationTimestamp: BigInt(task.initializationTimestamp),
    },
    {
      ...taskResponse,
      intent: { ...taskResponse.intent, value: BigInt(taskResponse.intent.value), chainId: BigInt(taskResponse.intent.chainId) },
      initializationTimestamp: BigInt(taskResponse.initializationTimestamp),
    },
    blsSignature,
  ],
  account: userAddress,
  chain: sepolia,
})
```

`validateAndExecuteDirect` verifies the attestation onchain and executes the call only if the policy passed.

## Unlinking

To remove a user's identity link from your app, call `unlinkApp`:

```typescript
await walletClient.unlinkApp({
  appWalletAddress: userWalletAddress,
  appClientAddress: yourPolicyClientAddress,
  appIdentityDomain: '0xa29cad97d4110e8822dedef32879f84a8e8889bf965ff9c57998689e2fdafbc4',
})
```

Unlinking removes the user's identity association with your policy client. Once unlinked, any policy evaluation that depends on this link will fail until the user re-links. Make it easy for users to find and trigger this action in your app; identity links should be as easy to remove as they are to create.

## Reference Pages

<Cards>
  <Card icon="code" to="/developers/verified-credential/reference" title="SDK & Contract Reference">
    Full SDK method signatures and contract functions
  </Card>

  <Card icon="brackets-curly" to="/developers/verified-credential/identity-policy-reference" title="Identity Policy Reference">
    All Rego built-in functions for identity checks
  </Card>
</Cards>

<Card icon="comment-question" to="https://calendly.com/d/cxcy-vqp-9cm/meet-with-the-newton-team" title="Have questions?">
  Get in touch with someone from the Newton team to discuss your integration
</Card>
