> For the complete documentation index, see [llms.txt](https://docs.toolkit.equs.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.toolkit.equs.ai/standards/agentic/ap2/flow-1/embed-ui-and-api.md).

# Method 1: Embed UI + API

Hand the User's approval to Embed UI; your back end creates the mandate request and verifies the response

Embed UI takes over the User's part: it opens their EQUS Web Wallet over your page, shows what your agent is asking for, and hands the approved response back. Your back end still creates the mandate request, with your agent's public key, and still verifies the response.

This method uses the EQUS Web Wallet. To reach the User in another wallet, build the hand-off yourself - see [Method 2: Build Your Own UI](/standards/agentic/ap2/flow-1/build-your-own-ui.md).

**Before you start** you need:

* A Verifier and a single-input VP Schema - see [Agentic Payments (AP2)](/standards/agentic/ap2.md).
* Your agent's public key - see [Flow 1](/standards/agentic/ap2/flow-1.md).
* Embed UI installed and initialized - see [Embedded UI](/standards/embedded-ui/overview.md).
* The Web Wallet enabled for you. It's off by default and available on request.
* The User **signed in to the Web Wallet inside Embed UI** before you ask: a framed wallet can't sign itself in. See [Embedding Web Wallet](/standards/embedded-ui/embedding-web-wallet.md).

### What the User goes through

1. **Start.** Something on your page - usually a button such as *Authorize agent* - calls `presentVCs`, and the Web Wallet opens over your page.
2. **Review.** They see your summary of the mandate and the credential it will be signed over.
3. **Approve** - or decline, in which case nothing is authorized. Either way the wallet closes and your page carries on. No code to type, no app switch.

{% hint style="warning" %}
**Call `presentVCs` synchronously from the User's click.** It opens a window, and browsers only allow that inside a user gesture. Any work you need to do beforehand belongs inside `getReqUriAndTxId`, which Embed UI calls for you once it's open.
{% endhint %}

### Who calls what

The two calls that carry your credentials - creating the request and verifying the response - go through your back end:

{% code collapsedlinecount="1000" %}

```mermaid
flowchart LR
    E["Embed UI<br/>in your page"] --> B["Your back end"] --> P["Platform<br/>Integration API"]
    style E fill:#e6f4ea,stroke:#34a853
    style B fill:#e8f0fe,stroke:#4285f4
    style P fill:#fff4e5,stroke:#f9ab00
```

{% endcode %}

### What you build

#### Creating the request

`presentVCs` takes a single options object, and one of its properties is `getReqUriAndTxId` - an async function you write. Embed UI calls it when the User starts. Return your request in the `web` slot and leave the others `null`:

{% code collapsedlinecount="1000" %}

```typescript
{ web: { requestUri, txId }, mobile: null, dc: null }
```

{% endcode %}

Inside it, have your back end create the mandate request:

{% hint style="info" icon="rectangle-api" %}
All calls mentioned on this page are on the [Integration API](https://docs.toolkit.equs.ai/standards/agentic/ap2/flow-1/pages/MvAvsnLQU3ER2hOIIDU2#id-2.4.1-apis).
{% endhint %}

## \[Experimental] Create AP2 mandate request

> Create an OID4VP authorization request carrying one or more AP2 mandates, without needing to construct a DCQL query or handle dSD-JWT delegation directly.

```json
{"openapi":"3.0.0","info":{"title":"EqusDTK Integration API Documentation","version":"0.1.2"},"tags":[{"name":"Oid4Vp","description":""}],"security":[{"sp":[]},{"tenant":[]},{"sp":[]}],"components":{"securitySchemes":{"sp":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Service Provider. Token of the Service Provider client in the `agents` realm, with no `tenantId` claim. Authorize one bearer scheme at a time."},"tenant":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Tenant. Token of a Tenant machine client or of a Tenant portal user, carrying the `tenantId` claim. Authorize one bearer scheme at a time."}},"schemas":{"CreateMandateRequestRequest":{"type":"object","properties":{"mode":{"type":"string","description":"Mechanism used by the wallet to deliver the authorization response. Only encrypted modes are supported.","enum":["direct_post.jwt","dc_api.jwt"]},"vpSchemaId":{"type":"string","description":"The VP Schema to build the DCQL query against. Tenants create their own."},"mandates":{"description":"One or more AP2 mandates. All mandates must be Open, or all must be Closed — mixing is not supported.","type":"array","items":{"$ref":"#/components/schemas/Ap2BaseMandate"}},"agentKey":{"description":"Public JWK confirmation key for the delegate, embedded as cnf on Open mandates. Required when any mandate is Open; forbidden when every mandate is Closed.","allOf":[{"$ref":"#/components/schemas/PublicJWK"}]},"redirectUri":{"type":"string","description":"Same-device redirect target after the delegating wallet responds."},"verifierWebUrl":{"type":"string","description":"Web hand-off / QR surface the delegating human lands on."},"expectedOrigins":{"description":"Web origins (http or https) expected for a Digital Credentials API (DC API) response mode.","type":"array","items":{"type":"string"}}},"required":["mode","vpSchemaId","mandates"]},"Ap2BaseMandate":{"type":"object","properties":{"vct":{"enum":["mandate.checkout.open.1","mandate.checkout.1","mandate.payment.open.1","mandate.payment.1"],"type":"string","description":"Mandate type discriminator (the verifiable credential type)."},"exp":{"type":"number","minimum":0,"description":"Unix epoch. Defaults to iat + the configured mandate TTL when omitted."}},"required":["vct"]},"PublicJWK":{"type":"object","properties":{"kty":{"type":"string","description":"JWK key type, naming the key family the other members belong to."},"crv":{"type":"string","description":"Cryptographic curve the key uses. Present for EC and OKP key types."},"x":{"type":"string","description":"Base64url-encoded x coordinate. Present for EC and OKP key types; for OKP it carries the whole public key."},"y":{"type":"string","description":"Base64url-encoded y coordinate. Present for EC key types only."},"n":{"type":"string","description":"Base64url-encoded modulus. Present for RSA key types only."},"e":{"type":"string","description":"Base64url-encoded public exponent. Present for RSA key types only."},"use":{"type":"string","enum":["sig","enc"],"description":"JWK public key use."},"key_ops":{"description":"JWK key operations.","type":"array","items":{"type":"string"}},"alg":{"type":"string","description":"Cryptographic algorithm intended for use with this key."},"kid":{"type":"string","description":"JWK key ID, identifying the key within the JWK itself."},"x5u":{"type":"string","format":"uri","description":"URL of the X.509 certificate chain for this key."},"x5c":{"description":"X.509 certificate chain for this key.","type":"array","items":{"type":"string"}},"x5t":{"type":"string","description":"X.509 certificate SHA-1 thumbprint."},"x5t#S256":{"type":"string","description":"X.509 certificate SHA-256 thumbprint."}},"required":["kty"]},"CreateMandateRequestResponse":{"type":"object","properties":{"requestUri":{"type":"string","description":"The OID4VP request URI to render as a QR code or embed in a deep link for the delegating human."},"txId":{"type":"string","description":"Correlation id for the eventual verification call."}},"required":["requestUri","txId"]}}},"paths":{"/api/v1/{verifierId}/oid4vp/requests/ap2":{"post":{"description":"Create an OID4VP authorization request carrying one or more AP2 mandates, without needing to construct a DCQL query or handle dSD-JWT delegation directly.","operationId":"Oid4VpController_createMandateRequest_api/v1","parameters":[{"name":"verifierId","required":true,"in":"path","description":"The verifier to create the mandate request against.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMandateRequestRequest"}}}},"responses":{"200":{"description":"When the AP2 mandate request is created successfully. The uri for the authorization request and corresponding txId is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMandateRequestResponse"}}}},"400":{"description":"When mandate content fails validation against its vct schema, mixes open and closed mandates, is missing a required agentKey, an Open Checkout Mandate is missing a checkout.line_items constraint, a closed Checkout/Payment mandate pair does not link correctly."},"404":{"description":"When no VP Schema exists with such id."}},"summary":"[Experimental] Create AP2 mandate request","tags":["Oid4Vp"]}}}}
```

Use `mode: "direct_post.jwt"`, and set `redirectUri` to your page - the one that hosts Embed UI. The API doesn't require `redirectUri`, but Embed UI does: it's how the response finds its way back.

For a Human-not-present approval, send Open mandates and your agent's public key:

{% code collapsedlinecount="1000" %}

```json
{
  "mode": "direct_post.jwt",
  "vpSchemaId": "<vpSchemaId>",
  "mandates": [
    {
      "vct": "mandate.checkout.open.1",
      "constraints": [
        { "type": "checkout.line_items",
          "items": [{ "id": "line_1", "quantity": 1,
                      "acceptable_items": [{ "id": "sku_1", "title": "Red Sneakers", "price": 12999 }] }] },
        { "type": "checkout.allowed_merchants",
          "allowed": [{ "id": "merch_1", "name": "Demo Merchant", "website": "https://merchant.example" }] }
      ]
    },
    {
      "vct": "mandate.payment.open.1",
      "constraints": [
        { "type": "payment.amount_range", "currency": "USD", "min": 0, "max": 50000 },
        { "type": "payment.allowed_payees", "allowed": [{ "id": "merch_1", "name": "Demo Merchant" }] }
      ]
    }
  ],
  "agentKey": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…", "alg": "ES256" },
  "redirectUri": "https://shop.example/checkout"
}
```

{% endcode %}

For a **Human-present** approval, send Closed mandates only and leave `agentKey` out - see [Mandate Reference](/standards/agentic/ap2/mandate-reference.md) for their shape. Every mandate is validated before anything reaches the wallet; an invalid one is rejected with a specific error.

#### Verifying the response

Embed UI hands the response to your `onSuccess` callback:

{% code collapsedlinecount="1000" %}

```typescript
{
  authResponse: { type, vpToken?, jwe? } | null,
  txId: string,
}
```

{% endcode %}

Send that to your own back end and verify it there, passing the `authResponse` and the `txId`:

## Verify Presentation

> Verify submitted Authorization Response and return verification result

```json
{"openapi":"3.0.0","info":{"title":"EqusDTK Integration API Documentation","version":"0.1.2"},"tags":[{"name":"Oid4Vp","description":""}],"security":[{"sp":[]},{"tenant":[]},{"sp":[]}],"components":{"securitySchemes":{"sp":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Service Provider. Token of the Service Provider client in the `agents` realm, with no `tenantId` claim. Authorize one bearer scheme at a time."},"tenant":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Tenant. Token of a Tenant machine client or of a Tenant portal user, carrying the `tenantId` claim. Authorize one bearer scheme at a time."}},"schemas":{"VerifyPresentationRequest":{"type":"object","properties":{"authResponse":{"description":"Authorization response returned by the wallet containing the presentation.","allOf":[{"$ref":"#/components/schemas/AuthResponse"}]},"txId":{"type":"string","description":"Transaction unique ID"},"origin":{"type":"string","description":"Web origin (http or https) the presentation response was submitted from."}},"required":["authResponse","txId"]},"AuthResponse":{"type":"object","properties":{"vpToken":{"type":"object","description":"Plain verifiable presentation token. Mutually exclusive with jwe."},"jwe":{"type":"string","description":"Encrypted JWE containing the authorization response. Mutually exclusive with vpToken."},"transactionDataResponse":{"type":"object","description":"Hashes of the transaction data items the wallet signed over."},"type":{"type":"number","description":"Indicates whether the authorization response is plain or encrypted."}},"required":["type"]},"VerifyPresentationResponse":{"type":"object","properties":{"verifiedClaims":{"type":"object","description":"Claims disclosed and verified from the holder-presented credential."},"presentations":{"type":"object","description":"Raw verifiable presentations submitted by the holder, keyed by credential."}},"required":["verifiedClaims"]}}},"paths":{"/api/v1/{verifierId}/oid4vp/verify":{"post":{"description":"Verify submitted Authorization Response and return verification result","operationId":"Oid4VpController_verifyPresentation_api/v1","parameters":[{"name":"verifierId","required":true,"in":"path","description":"Verifier unique ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyPresentationRequest"}}}},"responses":{"200":{"description":"When Presentation is verified successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyPresentationResponse"}}}},"400":{"description":"- When the session advertised a response encryption key but the Authorization Response was sent in plain, encrypted under a foreign key, or carried a malformed JWE.\n- When the origin supplied for a DC API verification is not one the Authorization Request declared in its expected origins."},"409":{"description":"When the verification session was verified, failed, deleted or expired by another request while this presentation was being verified."},"422":{"description":"When the access certificate the verification session was created under can no longer be resolved, or has since been revoked."},"500":{"description":"When a DC API verification session was stored without the expected origins its Authorization Request declared."}},"summary":"Verify Presentation","tags":["Oid4Vp"]}}}}
```

The platform verifies the presentation and the delegation chain in one go: the Issuer's signature on the User's credential, and the mandates the User signed over it. `transactionDataResponse` reports what the User actually signed.

### Start the approval

{% code collapsedlinecount="1000" %}

```javascript
import { presentVCs } from '@equs-ai/dtk-embedded-ui'

presentVCs({
  getReqUriAndTxId: async () => {
    const { requestUri, txId } = await requestMandate() // your back end calls POST .../oid4vp/requests/ap2
    return { web: { requestUri, txId }, mobile: null, dc: null }
  },
  walletPlatform: ['web'],
  dcApiEnabled: false,
  hidePresentationOverview: true,
  vpPurposeText: 'Authorize the agent to buy Red Sneakers from Demo Merchant, paying at most USD 500.00.',
  onSuccess: ({ authResponse, txId }) => verifyOnYourBackEnd(txId, authResponse),
  onReject: () => { /* the User declined - nothing was authorized */ },
  onError: (error) => { /* show the failure */ },
})
```

{% endcode %}

### Keep the grant

For Open mandates, what you've verified is the **grant**: the User's credential with the signed mandates on top, bound to your agent's key. Store it with the agent - the platform keeps nothing. When a merchant offers a checkout, your agent closes it with its own key; see [Flow 1](/standards/agentic/ap2/flow-1.md).

For Closed mandates there is nothing left to do: the verified artifact **is** the Closed mandate, ready to show a merchant or payment provider.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.toolkit.equs.ai/standards/agentic/ap2/flow-1/embed-ui-and-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
