---
title: "Freeze or block a lost card"
description: "Freeze a card while it is missing, unfreeze it when the cardholder finds it, or block a stolen card for good and issue a replacement on the same balance."
---
Use this recipe to freeze a card while it is missing and unfreeze it when the cardholder finds it. If the card is stolen, block it for good and issue a replacement that spends the same balance.

**Who it is for.** Any issuer whose cardholder taps "freeze card" or reports the card stolen.

**Status.** Available.

## Money example

Ada's card holds 131.25 USD with a 300 USD daily limit.

1. Ada cannot find her phone, so you freeze the card. `balance` shows 0, and `group_balance` still shows 131.25.
2. Ada finds her phone, so you unfreeze the card. `balance` is 131.25 again.
3. Later the card is stolen. You block it and issue a new card, which spends the same 131.25 USD.

## Before you start

Read the card. `GET /v1/cards/{card_id}` returns the current `status` and `spending_controls.daily_transaction_limit`. Every update must send that limit back.

The table lists the status changes the API allows.

| From | Allowed moves |
| :-- | :-- |
| `active` | `freeze`, `blocked` |
| `freeze` | `active`, `blocked` |
| `inactive` | `active`, `blocked` |
| `blocked` | None. `blocked` is final |

If your API key is scoped, it needs `card:freeze` or `card:terminate`.

## Find out what is required

A freeze is reversible, and a block is permanent. If the card has money of its own and you will **not** replace it, withdraw the money first, because you cannot withdraw from a blocked card. See [Close a card and get the money back](/recipes/cards/close-a-card-and-get-the-money-back).

## Steps

### 1. Freeze

<CodeGroup>
```bash title="cURL"
curl --request PATCH \
     --url https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --data '
{
  "status": "freeze",
  "limit": 300
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update", {
  method: "PATCH",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "status": "freeze",
    "limit": 300
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.patch(
    "https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
        "status": "freeze",
        "limit": 300,
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

### 2. Unfreeze

<CodeGroup>
```bash title="cURL"
curl --request PATCH \
     --url https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --data '
{
  "status": "active",
  "limit": 300
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update", {
  method: "PATCH",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "status": "active",
    "limit": 300
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.patch(
    "https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
        "status": "active",
        "limit": 300,
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

### 3. Block a stolen card

<CodeGroup>
```bash title="cURL"
curl --request PATCH \
     --url https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --data '
{
  "status": "blocked",
  "limit": 300
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update", {
  method: "PATCH",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "status": "blocked",
    "limit": 300
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.patch(
    "https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
        "status": "blocked",
        "limit": 300,
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

### 4. Issue a replacement

Send a new `Idempotency-Key` and no `funding`, so the new card joins the same balance.

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/cards \
     --header 'Api-Key: <API_KEY>' \
     --header 'Idempotency-Key: 5c653268-8d8a-44e9-a6d7-a03a52a2b518' \
     --header 'Content-Type: application/json' \
     --data '
{
  "customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
  "card_type": "virtual",
  "card_limit": 300,
  "name_on_card": "Ada Obi"
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/cards", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Idempotency-Key": "5c653268-8d8a-44e9-a6d7-a03a52a2b518",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
    "card_type": "virtual",
    "card_limit": 300,
    "name_on_card": "Ada Obi"
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/cards",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Idempotency-Key": "5c653268-8d8a-44e9-a6d7-a03a52a2b518",
        "Content-Type": "application/json",
    },
    json={
        "customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
        "card_type": "virtual",
        "card_limit": 300,
        "name_on_card": "Ada Obi",
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "message": "Card creation request is processing",
    "status": true,
    "id": "f4c3b2a1-0e9d-4c8b-a7f6-e5d4c3b2a190",
    "fee_charged": false
  },
  "error": null
}
```

The new card has a new id, number and PIN.

### 5. Check the balance

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url https://api.gravv.xyz/v1/cards/f4c3b2a1-0e9d-4c8b-a7f6-e5d4c3b2a190/balance \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/cards/f4c3b2a1-0e9d-4c8b-a7f6-e5d4c3b2a190/balance", {
  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/v1/cards/f4c3b2a1-0e9d-4c8b-a7f6-e5d4c3b2a190/balance",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

`balance` is 131.25. `shared` reads `true`, because the blocked card still counts in the group.

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| 400 `card is blocked and cannot be updated` | The card is already blocked | Issue a new card |
| 400 `internal server error` on a freeze | The status change is not allowed, such as `inactive` to `freeze` | Use an allowed change. Read the card back, and contact support if it looks wrong |
| 400 `an Idempotency-Key is required to issue an additional card` | You sent the replacement request without a key | Send a new key |
| 400 `Idempotency-Key was already used for a different request` | You reused a key with another body | Use a new key |
| 403 `forbidden`, `required_any` `card:freeze`, `card:terminate` | The scoped key has no card permissions | Grant `card:freeze` or `card:terminate` |
| The limit is lost after a freeze | You sent the update without `limit` | [Set it again](/recipes/cards/change-a-cards-daily-limit) |

## Webhooks you will receive

You receive `cards.status.update` for every real change, with `event_data.card.status` (`freeze`, `active`, `blocked`), `card.last4` and `card.id`. Gravv sends no webhook for a call that keeps the same status.

```json title="cards.status.update"
{
  "event_category": "cards",
  "event_type": "cards.status.update",
  "event_group_id": "a14ec356-f33d-4384-ba68-4d9bfa19765b",
  "event_data": {
    "card": {
      "customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
      "id": "a14ec356-f33d-4384-ba68-4d9bfa19765b",
      "last4": "9351",
      "name_on_card": "Ada Obi",
      "network": "visa",
      "status": "freeze"
    }
  }
}
```

## Testing in sandbox

Every status change works in sandbox. Gravv sends one `cards.status.update` for each real change, 20 to 60 seconds later. A frozen card shows `balance` 0 and the full `group_balance`.

## Reference

- [Manage card status](/platform/cards/manage-card-status)
- [Create a card](/platform/cards/create-a-card)
