# Manage intents and approvals

Use this page for day-to-day intent submission, review, approval, rejection, dry runs, and state checks. For the concept, see [Intents and approvals](/products/custody/governance/intents).

## Intent actions

| Action | UI procedure | API procedure |
|  --- | --- | --- |
| Request a change | [Request a change in the UI](#request-a-change-in-the-ui) | [Submit an intent with the API](#submit-an-intent-with-the-api) |
| Submit a system-signed proposal | Not available in the UI. | [Submit a system-signed intent with the API](#submit-a-system-signed-intent-with-the-api) |
| Dry run an intent | Use the API procedure. | [Dry run an intent with the API](#dry-run-an-intent-with-the-api) |
| Approve or reject an intent | [Approve or reject an intent in the UI](#approve-or-reject-an-intent-in-the-ui) | [Approve or reject an intent with the API](#approve-or-reject-an-intent-with-the-api) |
| Check request, intent, entity, transaction state, and remaining approvers | [Check state in the UI](#check-state-in-the-ui) | [Check state with the API](#check-state-with-the-api) |


## Intent workflow

During initial system setup, the `POST /v1/genesis` API operation creates the initial environment and entities without authentication or governance. After that initial setup, changes to system data follow the intent workflow.

1. Dry run the payload when you are using the API or testing a high-risk payload.
2. Submit the intent. Most submissions are user-signed. API service callers can submit system-signed proposals when the deployment and policies allow it.
3. Review the pending intent.
4. Approve or reject.
5. Check request, intent, entity, and transaction state.


## Request a change in the UI

Every change you make to system data in the UI creates an intent. For example, creating a domain, creating a user, registering an endpoint, or sending assets all follow the same high-level pattern:

1. Navigate to the relevant UI area.
2. Enter the change details.
3. Click **Submit for approval**.
4. Sign the operation with the **Ripple Custody: Auth & Sign** app. For signing instructions, see [Sign UI operations](/products/custody/identity-and-access/authentication/ui-authentication#sign-ui-operations).
5. Track the intent until it is approved, rejected, expired, or executed.


For UI and other user-signed submissions, the submitter of the intent acts as the first approver. If the selected policy includes an approval workflow that does not match the submitter's user role, the intent fails.

## Dry run an intent with the API

Dry runs simulate user-signed intent submission so you can understand whether a payload is valid before proposing and signing it.

Use dry runs for:

- Policy, domain, user, and user role-assignment changes.
- High-risk or complex transactions.
- Payloads where condition or workflow behavior is uncertain.
- Payloads that depend on ledger-specific validation, balances, destinations, or fees.


To perform a dry run:

1. Prepare the request body in the format shown in [Dry run and signature request body](/products/custody/governance/intents/intent-request-structure#dry-run-and-signature-request-body).
2. Call the [Perform a dry run](/products/custody/reference/api/openapi/intents/intentdryrun) operation.
3. Review the `success`, `errors`, and, for transaction intents, `hint` fields.



```sh
curl -X POST "${CUSTODY_API_URL}/v1/intents/dry-run" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @intent-dry-run.json
```

The dry-run request body uses the user-signed proposal shape and includes an `author`. The API specification does not define a separate system-signed dry-run body.

## Submit an intent with the API

The [Propose an intent](/products/custody/reference/api/openapi/intents/createintent) operation is the entry point for state mutation API requests. This procedure describes the standard user-signed proposal flow.

To submit an intent:

1. Prepare the intent request object with the author, expiry, target domain, intent ID, payload, description, custom properties, and `type: "Propose"`.
2. Canonicalize and sign the request object. For signing details, see [Authenticate API requests](/products/custody/identity-and-access/authentication/authenticate-api-requests#signing-intents-state-mutation-operations).
3. Create the full request body with the signed `request` object and `signature`.
4. Call [Propose an intent](/products/custody/reference/api/openapi/intents/createintent) with the signed request body.



```sh
curl -X POST "${CUSTODY_API_URL}/v1/intents" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -H "requestId: ${REQUEST_ID}" \
  -d @signed-intent.json
```

The `requestId` header is optional but recommended for traceability. You can use it to check request state.

The operation returns `202 Accepted` with an intent response when the request is accepted for asynchronous processing. This response means the workflow is scheduled. It does not mean the intent has executed or that the resulting entity or transaction state has changed.

## Submit a system-signed intent with the API

System-signed intent submission is for service callers that are allowed to propose specific intent types without sending a client-side payload signature. The request is still authenticated with a bearer token, signed internally by the platform, and governed by policies.

Before using this flow, confirm that:

- System-signed intent configuration is available for the deployment.
- The active Gateway system signing public key is registered in Notary. To discover the active public key, call `GET /v1/system-signing/info`. For more information, see [System signing info endpoint](/products/custody/deployment/reference/system-signed-intents#system-signing-info-endpoint).
- `NOTARY_SYSTEM_SIGNED_INTENTS_ENABLED` is set to `enabled: true`.
- The service caller can [obtain a bearer token](/products/custody/identity-and-access/authentication/authenticate-api-requests#obtain-a-token-for-a-service-caller) accepted by the API.
- A policy with `intentOrigin: "SystemSigned"` matches the target domain, intent type, and service caller.


System-signed intent configuration is enabled by default at deployment level. This does not activate system-signed traffic. Until key registration, the runtime system property, and matching policies are in place, system-signed proposals fail closed.

Prepare a request body with `request.type: "SystemSigned"`. Do not include `request.author` or a top-level `signature`.


```json
{
  "request": {
    "type": "SystemSigned",
    "targetDomainId": "9067d363-6411-498b-a32b-15d230a86706",
    "id": "00baa536-421e-11ee-be56-0242ac120002",
    "expiryAt": "2026-06-30T15:30:00.000Z",
    "payload": {
      "type": "v0_CreateTransactionOrder",
      "...": "payload-specific fields"
    },
    "description": "Service-submitted transaction order",
    "customProperties": {}
  }
}
```

Submit the request:


```sh
curl -X POST "${CUSTODY_API_URL}/v1/intents" \
  -H "Authorization: Bearer ${SERVICE_JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -H "requestId: ${REQUEST_ID}" \
  -d @system-signed-intent.json
```

The response uses the same `202 Accepted` response shape as user-signed proposal submission. When you retrieve the intent or request state, service-authored records identify the submitter with `author.subject` or `requester.subject`. For how these relate to `submitter.subject` in policy conditions, see [Service-caller identity](/products/custody/identity-and-access/authentication/authenticate-api-requests#service-caller-identity-submitter-author-and-requester).

Approvals and rejections stay user-signed
System-signed submission applies to proposals only. The service submitter does not count as a user approval. If the selected system-signed policy includes a workflow, user-signed approvers must satisfy it.

## Approve or reject an intent in the UI

You can approve or reject intents that are relevant for your user. The intents you can approve or reject depend on your user role and the policies that apply in the domain hierarchy.

To approve or reject an intent:

1. Navigate to **Intents**.
2. Select the **Waiting for decision** tab.
3. Select the arrow next to the intent's status to open the decision workflow panel.
4. Review the intent details, selected policy, approval workflow, and any compliance or incident notes.
5. Scroll to the end of the workflow and select **Approve** or **Reject**.
6. Enter the reason. The reason is mandatory for rejections and optional for approvals.
7. Sign the operation with the **Ripple Custody: Auth & Sign** app.


If you approve the intent, it continues through the workflow. When all required steps are complete, the intent can execute. If you reject the intent, the status changes to **Rejected**.

## Approve or reject an intent with the API

To approve or reject an intent with the API:

1. Retrieve the intent details with [List intents](/products/custody/reference/api/openapi/intents/getintents) or [Get intent](/products/custody/reference/api/openapi/intents/getintent).
2. Prepare the approval or rejection request body. Use the intent ID and the `proposalSignature` from the intent details.
3. Canonicalize and sign the request object. For signing details, see [Authenticate API requests](/products/custody/identity-and-access/authentication/authenticate-api-requests#signing-intents-state-mutation-operations).
4. Call [Approve intent](/products/custody/reference/api/openapi/intents/approveintent) or [Reject intent](/products/custody/reference/api/openapi/intents/rejectintent).


Approval and rejection API calls also return `202 Accepted` when the signed decision is accepted for asynchronous processing. Check the request or intent state to confirm whether the workflow processed the decision successfully.

Approve example:


```json
{
  "request": {
    "author": {
      "id": "828b554c-c9c9-11eb-a79b-dcfb48cfb3cb",
      "domainId": "455ad43e-cdd9-11eb-8465-dcfb48cfb3cb"
    },
    "targetDomainId": "455ad43e-cdd9-11eb-8465-dcfb48cfb3cb",
    "intentId": "846b5c3c-27e3-4146-ab18-ce6732624d35",
    "proposalSignature": "<PROPOSAL_SIGNATURE>",
    "approvalReason": "Approved after review",
    "type": "Approve"
  },
  "signature": "<APPROVAL_SIGNATURE>"
}
```


```sh
curl -X POST "${CUSTODY_API_URL}/v1/intents/approve" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @signed-approval.json
```

For rejection, use `type: "Reject"` and include `rejectReason`.

## Check state

Intent workflows have several state checks:

### Check state in the UI

| Check | UI procedure |
|  --- | --- |
| Request state | Use request lists and status messages in the UI. |
| Intent state | Navigate to **Intents** and review the intent status and workflow details. |
| Remaining approvers | Review the approval workflow in the UI. |
| Entity state | Open the created or updated entity in the relevant UI area. |
| Transaction state | View transaction, transfer, and order tabs for the account. |


### Check state with the API

| Check | API procedure |
|  --- | --- |
| Request state | Call [Get request state](/products/custody/reference/api/openapi/requests/getrequeststate) with the `requestId`. |
| Intent state | Call [Get intent](/products/custody/reference/api/openapi/intents/getintent) with the intent ID. |
| Remaining approvers | Call [Get remaining users](/products/custody/reference/api/openapi/intents/getremainingusers). |
| Entity state | Call the relevant entity API, such as [Get current user](/products/custody/reference/api/openapi/users/getme), [Get domain details](/products/custody/reference/api/openapi/domains/getdomain), or [Get account details](/products/custody/reference/api/openapi/accounts/getaccount). |
| Transaction state | Use [View transactions with the API](/products/custody/transactions/viewing-assets/view-and-audit-api). |


Any authorized rejection stops the workflow. Expired intents do not execute and must be recreated if the change is still needed.

For service-authored records, [List intents](/products/custody/reference/api/openapi/intents/getintents) supports filtering with `details.author.subject`.

## Review checklist

Before approving an intent, check:

- Target domain.
- Intent type.
- Payload.
- Selected policy.
- Required workflow.
- Remaining approvers.
- Expiry time.
- Compliance or incident notes.


For payload fields, see [Intent reference](/products/custody/governance/intents/reference).