---
title: "Unblock an application"
description: "Find out what the bank is waiting for on a pending v2 account, then send the documents or data in one PATCH, or send the customer to the bank's link."
---
Find out what the bank is waiting for on a pending v2 account. Then send the documents or data in one PATCH, or send the customer to the bank's link.

**API version:** v2.

**Who it is for.** Any developer whose new account is still `pending` with `requires_user_action: true`, or who received `applications.accounts.blocked`.

**Status.** Available.

## Money example

Ada's client cannot pay her 1,200.00 USD invoice until her account is active.

1. The bank asks for a newer proof of address. The account stays `pending`.
2. You upload one PDF with PATCH. The application goes back to `processing`.
3. The bank approves the application, the account becomes `active`, and the client can send the 1,200.00 USD.

## Before you start

You have the `account_id` from the create response, or the `customer_id`.

## Find out what is required

### 1. Find the application

Read `application_id` from `GET /v2/accounts/{account_id}`, or list the customer's blocked applications:

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url 'https://api.gravv.xyz/v2/accounts/applications?customer_id=0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33&status=blocked' \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v2/accounts/applications?customer_id=0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33&status=blocked", {
  method: "GET",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.gravv.xyz/v2/accounts/applications?customer_id=0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33&status=blocked",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

Page through the list with `limit` and `offset`. Its `count` is the size of the current page, not a total. The `status` filter is lower case, and a misspelled status returns `[]`.

### 2. Read what is asked

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6 \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6", {
  method: "GET",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "application_id": "5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6",
    "customer_id": "0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33",
    "customer_type": "individual",
    "status": "blocked",
    "parties": [{"role": "primary", "status": "declared", "first_name": "Ada", "last_name": "Okafor"}],
    "created_at": "2026-10-13T09:12:44.512031Z",
    "updated_at": "2026-10-13T09:20:02.118734Z",
    "status_reason": "awaiting_customer_document",
    "status_message": "The bank is waiting on documents for this customer. Upload every type listed in required_documents in ONE PATCH /v2/accounts/applications/{application_id} call — separate calls make the bank re-review what it already holds.",
    "required_documents": ["proof_of_address"],
    "requires_user_action": true
  },
  "error": null
}
```

Branch on `status_reason`. Never branch on `status_message`, because its text can change.

| `status_reason` | Who acts | What to do |
| :-- | :-- | :-- |
| `awaiting_customer_document` | You | PATCH every type in `required_documents`, in **one** call |
| `awaiting_customer_data` | You | PATCH every field in `required_fields`, as `metadata` keys |
| `tos_pending` | The customer | Open `required_action_url` and accept the bank's terms. The application resumes on its own. The API has no call for terms |
| `kyc_pending` | The customer | Open `required_action_url`, the bank's own verification page. If it is not there yet, read the application again later |
| `provider_review`, `awaiting_activation_details`, `provider_unavailable`, `provisioning_retrying` | Nobody | Wait |
| `manual_review_requested` | Gravv | Gravv holds the application for review and sends no webhook. Contact support with `account_id` and `application_id` |
| `verification_rejected`, `verification_declined`, `review_declined` | Nobody | The bank said no, and the same details get the same answer. Try another option |
| `region_not_supported` | Nobody | Choose another `bank_code` |
| `identity_document_mismatch`, `information_expired`, `request_cancelled` | You | Open a new account with corrected data |
| `settlement_wallet_unavailable` | You | Open a new account on another network |

An application's `status` is one of `processing`, `blocked`, `manual_review`, `approved`, `rejected`, `expired`, `cancelled`.

## Steps

### 3. Send what was asked, in one PATCH

PATCH takes no `Idempotency-Key`, so do not retry it without checking. After a timeout, read the application first. Each document needs `filename` here, and can be at most 2 MB. In sandbox, keep the files of one PATCH under 500 KB together.

<CodeGroup>
```bash title="cURL"
curl --request PATCH \
     --url https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6 \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --data '
{
  "documents": [
    {
      "document_type": "proof_of_address",
      "issuing_country": "GB",
      "filename": "council-tax-2026.pdf",
      "content": "<base64 of the document>"
    }
  ]
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6", {
  method: "PATCH",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "documents": [
      {
        "document_type": "proof_of_address",
        "issuing_country": "GB",
        "filename": "council-tax-2026.pdf",
        "content": "<base64 of the document>"
      }
    ]
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.patch(
    "https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
        "documents": [
            {
                "document_type": "proof_of_address",
                "issuing_country": "GB",
                "filename": "council-tax-2026.pdf",
                "content": "<base64 of the document>",
            },
        ],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

For missing data, send `metadata` instead:

<CodeGroup>
```bash title="cURL"
curl --request PATCH \
     --url https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6 \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --data '
{
  "metadata": {
    "tax_id_number": "123456789"
  }
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6", {
  method: "PATCH",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "metadata": {
      "tax_id_number": "123456789"
    }
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.patch(
    "https://api.gravv.xyz/v2/accounts/applications/5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
        "metadata": {
            "tax_id_number": "123456789",
        },
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

On a business application, an identity document names its person with `party_id`, taken from `parties[].id` on the application. It also carries `number` and `expiration_date`.

The API returns the application. Its status is `processing` if you sent everything the bank asked for, or still `blocked` if you did not. The response carries no `status_reason` or `required_*` fields, so read the application again.

### 4. Wait for the account

Wait for `applications.accounts.completed`, or read `GET /v2/accounts/{account_id}` until `status` is `active`.

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| 400 `application is not editable` | The application is `processing`, `approved` or `rejected` | PATCH only a `blocked` application, or a `manual_review` application for documents |
| 400 `Tax ID Number was not requested for this application` | You sent a field the bank did not ask for | Send only `required_fields` and `required_documents` |
| 400 `document type "articles_of_incorporation" was not requested for this application` | You sent a document the bank did not ask for | Send only `required_fields` and `required_documents` |
| 400 `missing document filename` | The document has no `filename` | Add it |
| 400 `missing document issuing country` | The document has no `issuing_country` | Add it |
| 400 `Party ID is required for a business party document` | A business ID has no `party_id` | Add it |
| 400 `Type is no longer accepted; send Document Type …` | You sent the old document keys | Send `document_type` and `document_type_side` |
| 400 `invalid request payload: unknown field "tos_accepted"` | The API has no terms field | Send the customer to `required_action_url` |
| 404 `Resource not found` | The id is wrong, or belongs to another environment | Check the id and the key |
| Still `blocked` after the PATCH | You sent only part of what the bank asked for | Read the application again and send the rest in one call |
| A second block of the same type never arrives as a webhook | Gravv can drop repeat events of one type for one application | Always read the application again after you act |
| No progress for 7 days on a customer block | The block expires. After three expiries, Gravv reviews the application | Act on each block before it expires, and contact support |

## Webhooks you will receive

Gravv sends `applications.accounts.blocked`, with `metadata.block_reason`, `metadata.action_url` and `metadata.additional_requirements` (`fields[]`, `documents[]`). Then it sends `applications.accounts.completed`, or `applications.accounts.failed` with `metadata.fail_reason`. Deduplicate events on the body's `event_id`. Do not use `X-Event-ID`, because it is the application id and is the same on every event. See [Account application events](/platform/webhooks/event-catalogue#account-applications).

## Testing in sandbox

For the wallet-backed options, blocks come from the banks' test systems, and you cannot force one. The PATCH flow is the same in sandbox.

## Reference

- [List account applications](/api-reference/accounts-v2/get-v2-accounts-applications)
- [Get an account application](/api-reference/accounts-v2/get-v2-accounts-applications-application-id)
- [Update an account application](/api-reference/accounts-v2/patch-v2-accounts-applications-application-id)
- [Fix a blocked application](/platform/accounts/fix-a-blocked-application)
