> This is Payabli documentation. For a complete page index, fetch https://docs.payabli.com/llms.txt — append .md to any page URL for lightweight markdown. For section-level indexes, query parameters, and other AI-optimized access methods, see https://docs.payabli.com/ai-agents.md

# Manage ghost cards with the API

> Learn how to create and manage multi-use virtual debit cards for vendor spend with the Payabli API

A ghost card is a multi-use virtual card for paying a specific vendor over time, rather than for a single payout. It suits recurring or discretionary vendor spend with configurable limits. This guide covers creating ghost cards and updating their status through the API. For how ghost cards compare to single-use virtual cards, see [Cards overview](/guides/pay-out-cards-overview).

## Considerations

When working with ghost cards, keep the following in mind:

* Only one ghost card can exist per vendor per paypoint. To issue a new card to the same vendor, cancel the existing card first.
* Ghost cards are linked to a vendor. The vendor must belong to the paypoint and have an active status.
* `expenseLimit` is required and must be greater than `0`. It can't exceed the paypoint's configured payout credit limit.
* Setting `exactAmount` to `true` forces `maxNumberOfUses` to `1`, regardless of any other value you pass.
* If you set `maxNumberOfUses` to `0` or a negative number, it defaults to `9999`.
* Card currency is always USD.

## How Payabli records ghost card charges

A ghost card can be charged many times, up to the limits set on the card. Payabli records each charge as a payout transaction, whether the charge is approved or declined. These transactions appear in your [Pay Out reporting](/guides/pay-out-portal-transactions-reports) alongside your other payouts, so you can query and report on them the same way.

You can also view ghost cards and their status in the Cards report under Pay Out in the Payabli Portal.

## Create a ghost card

Send a POST request to `/api/MoneyOutCard/GhostCard/{entry}` to create a ghost card. See the [API reference](/developers/api-reference/cards/create-ghost-card) for full documentation.

Most fields are required, including `vendorId`, `expenseLimit`, `amount`, `maxNumberOfUses`, `exactAmount`, `billingCycle`, `billingCycleDay`, `dailyTransactionCount`, `dailyAmountLimit`, and `transactionAmountLimit`. Optional fields include `expirationDate`, `mcc`, `tcc`, `misc1`, and `misc2`.

### Request

POST [https://api-sandbox.payabli.com/api/MoneyOutCard/GhostCard/\{entry}](https://api-sandbox.payabli.com/api/MoneyOutCard/GhostCard/\{entry})

```curl
curl -X POST https://api-sandbox.payabli.com/api/MoneyOutCard/GhostCard/8cfec329267 \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "vendorId": 456,
  "expenseLimit": 500,
  "amount": 500,
  "maxNumberOfUses": 3,
  "exactAmount": false,
  "expenseLimitPeriod": "monthly",
  "billingCycle": "monthly",
  "billingCycleDay": "1",
  "dailyTransactionCount": 5,
  "dailyAmountLimit": 200,
  "transactionAmountLimit": 100,
  "mcc": "5411",
  "tcc": "R",
  "misc1": "PO-98765",
  "misc2": "Dept-Finance"
}'
```

```typescript
import { PayabliClient } from "@payabli/sdk-node";

async function main() {
    const client = new PayabliClient({
        bearerAuth: {
            clientId: "YOUR_CLIENT_ID",
            clientSecret: "YOUR_CLIENT_SECRET",
        },
    });
    await client.ghostCard.createGhostCard("8cfec329267", {
        vendorId: 456,
        expenseLimit: 500,
        amount: 500,
        maxNumberOfUses: 3,
        exactAmount: false,
        expenseLimitPeriod: "monthly",
        billingCycle: "monthly",
        billingCycleDay: "1",
        dailyTransactionCount: 5,
        dailyAmountLimit: 200,
        transactionAmountLimit: 100,
        mcc: "5411",
        tcc: "R",
        misc1: "PO-98765",
        misc2: "Dept-Finance",
    });
}
main();

```

```python
from payabli import payabli

client = payabli(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
)

client.ghost_card.create_ghost_card(
    entry="8cfec329267",
    vendor_id=456,
    expense_limit=500,
    amount=500,
    max_number_of_uses=3,
    exact_amount=False,
    expense_limit_period="monthly",
    billing_cycle="monthly",
    billing_cycle_day="1",
    daily_transaction_count=5,
    daily_amount_limit=200,
    transaction_amount_limit=100,
    mcc="5411",
    tcc="R",
    misc_1="PO-98765",
    misc_2="Dept-Finance",
)

```

```java
package com.example.usage;

import io.github.payabli.api.PayabliApiClient;
import io.github.payabli.api.resources.ghostcard.requests.CreateGhostCardRequestBody;

public class Example {
    public static void main(String[] args) {
        PayabliApiClient client = PayabliApiClient.withCredentials("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
            .build()
        ;

        client.ghostCard().createGhostCard(
            "8cfec329267",
            CreateGhostCardRequestBody
                .builder()
                .vendorId(456L)
                .expenseLimit(500.0)
                .amount(500.0)
                .maxNumberOfUses(3)
                .exactAmount(false)
                .expenseLimitPeriod("monthly")
                .billingCycle("monthly")
                .billingCycleDay("1")
                .dailyTransactionCount(5)
                .dailyAmountLimit(200.0)
                .transactionAmountLimit(100)
                .mcc("5411")
                .tcc("R")
                .misc1("PO-98765")
                .misc2("Dept-Finance")
                .build()
        );
    }
}
```

```ruby
require "payabli"

client = Payabli::Client.new(api_key: "YOUR_API_KEY_HERE")

client.ghost_card.create_ghost_card(
  entry: "8cfec329267",
  vendor_id: 456,
  expense_limit: 500,
  amount: 500,
  max_number_of_uses: 3,
  exact_amount: false,
  expense_limit_period: "monthly",
  billing_cycle: "monthly",
  billing_cycle_day: "1",
  daily_transaction_count: 5,
  daily_amount_limit: 200,
  transaction_amount_limit: 100,
  mcc: "5411",
  tcc: "R",
  misc_1: "PO-98765",
  misc_2: "Dept-Finance"
)

```

```csharp
using PayabliApi;
using System.Threading.Tasks;

public partial class Examples
{
    public async Task Example() {
        var client = new PayabliApiClient(
            clientId: "YOUR_CLIENT_ID",
            clientSecret: "YOUR_CLIENT_SECRET"
        );

        await client.GhostCard.CreateGhostCardAsync(
            entry: "8cfec329267",
            request: new CreateGhostCardRequestBody {
                VendorId = 456L,
                ExpenseLimit = 500,
                Amount = 500,
                MaxNumberOfUses = 3,
                ExactAmount = false,
                ExpenseLimitPeriod = "monthly",
                BillingCycle = "monthly",
                BillingCycleDay = "1",
                DailyTransactionCount = 5,
                DailyAmountLimit = 200,
                TransactionAmountLimit = 100,
                Mcc = "5411",
                Tcc = "R",
                Misc1 = "PO-98765",
                Misc2 = "Dept-Finance"
            }
        );
    }

}

```

```go
package example

import (
    context "context"

    payabli "github.com/payabli/sdk-go"
    client "github.com/payabli/sdk-go/client"
    option "github.com/payabli/sdk-go/option"
)

func do() {
    client := client.NewClient(
        option.WithClientCredentials(
            "YOUR_CLIENT_ID",
            "YOUR_CLIENT_SECRET",
        ),
    )
    request := &payabli.CreateGhostCardRequestBody{
        VendorId: int64(456),
        ExpenseLimit: 500,
        Amount: 500,
        MaxNumberOfUses: 3,
        ExactAmount: false,
        ExpenseLimitPeriod: "monthly",
        BillingCycle: "monthly",
        BillingCycleDay: "1",
        DailyTransactionCount: 5,
        DailyAmountLimit: 200,
        TransactionAmountLimit: 100,
        Mcc: payabli.String(
            "5411",
        ),
        Tcc: payabli.String(
            "R",
        ),
        Misc1: payabli.String(
            "PO-98765",
        ),
        Misc2: payabli.String(
            "Dept-Finance",
        ),
    }
    client.GhostCard.CreateGhostCard(
        context.TODO(),
        "8cfec329267",
        request,
    )
}

```

```php
<?php

namespace Example;

use Payabli\PayabliClient;
use Payabli\GhostCard\Requests\CreateGhostCardRequestBody;

$client = new PayabliClient(
    clientId: 'YOUR_CLIENT_ID',
    clientSecret: 'YOUR_CLIENT_SECRET',
);
$client->ghostCard->createGhostCard(
    '8cfec329267',
    new CreateGhostCardRequestBody([
        'vendorId' => 456,
        'expenseLimit' => 500,
        'amount' => 500,
        'maxNumberOfUses' => 3,
        'exactAmount' => false,
        'expenseLimitPeriod' => 'monthly',
        'billingCycle' => 'monthly',
        'billingCycleDay' => '1',
        'dailyTransactionCount' => 5,
        'dailyAmountLimit' => 200,
        'transactionAmountLimit' => 100,
        'mcc' => '5411',
        'tcc' => 'R',
        'misc1' => 'PO-98765',
        'misc2' => 'Dept-Finance',
    ]),
);

```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "vendorId": 456,
  "expenseLimit": 500,
  "amount": 500,
  "maxNumberOfUses": 3,
  "exactAmount": false,
  "expenseLimitPeriod": "monthly",
  "billingCycle": "monthly",
  "billingCycleDay": "1",
  "dailyTransactionCount": 5,
  "dailyAmountLimit": 200,
  "transactionAmountLimit": 100,
  "mcc": "5411",
  "tcc": "R",
  "misc1": "PO-98765",
  "misc2": "Dept-Finance"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.payabli.com/api/MoneyOutCard/GhostCard/8cfec329267")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

A successful request returns a `ReferenceId` in `responseData`. This is the card token. Store it to reference the card in subsequent operations.

### Response (200)

```json
{
  "responseText": "Success",
  "isSuccess": true,
  "responseData": {
    "ReferenceId": "129-219",
    "ResultCode": 1,
    "ResultText": "Ghost Card created"
  }
}
```

## Update a card's status

Send a PATCH request to `/api/MoneyOutCard/card/{entry}` to update a card's status. See the [API reference](/developers/api-reference/cards/update-card-status) for full documentation.

Pass the `cardToken` (the `ReferenceId` from the create response) and the new `status`. Valid statuses are `Active`, `Inactive`, `Cancelled`, and `Expired`.

Not all status transitions are allowed:

| From        | Allowed transitions                |
| ----------- | ---------------------------------- |
| `Active`    | `Inactive`, `Cancelled`, `Expired` |
| `Inactive`  | `Active`                           |
| `Expired`   | `Active` (renews the card)         |
| `Cancelled` | None — `Cancelled` is terminal     |

The following example cancels a card by setting its status to `Cancelled`.

### Request

PATCH [https://api-sandbox.payabli.com/api/MoneyOutCard/card/\{entry}](https://api-sandbox.payabli.com/api/MoneyOutCard/card/\{entry})

**`CancelGhostCard`**

```curl CancelGhostCard
curl -X PATCH https://api-sandbox.payabli.com/api/MoneyOutCard/card/8cfec329267 \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "cardToken": "gc_abc123def456",
  "status": "Cancelled"
}'
```

**`CancelGhostCard`**

```typescript CancelGhostCard
import { PayabliClient } from "@payabli/sdk-node";

async function main() {
    const client = new PayabliClient({
        bearerAuth: {
            clientId: "YOUR_CLIENT_ID",
            clientSecret: "YOUR_CLIENT_SECRET",
        },
    });
    await client.ghostCard.updateCard("8cfec329267", {
        cardToken: "gc_abc123def456",
        status: "Cancelled",
    });
}
main();

```

**`CancelGhostCard`**

```python CancelGhostCard
from payabli import payabli

client = payabli(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
)

client.ghost_card.update_card(
    entry="8cfec329267",
    card_token="gc_abc123def456",
    status="Cancelled",
)

```

**`CancelGhostCard`**

```java CancelGhostCard
package com.example.usage;

import io.github.payabli.api.PayabliApiClient;
import io.github.payabli.api.resources.ghostcard.requests.UpdateCardRequestBody;
import io.github.payabli.api.types.CardStatus;

public class Example {
    public static void main(String[] args) {
        PayabliApiClient client = PayabliApiClient.withCredentials("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
            .build()
        ;

        client.ghostCard().updateCard(
            "8cfec329267",
            UpdateCardRequestBody
                .builder()
                .cardToken("gc_abc123def456")
                .status(CardStatus.CANCELLED)
                .build()
        );
    }
}
```

**`CancelGhostCard`**

```ruby CancelGhostCard
require "payabli"

client = Payabli::Client.new(api_key: "YOUR_API_KEY_HERE")

client.ghost_card.update_card(
  entry: "8cfec329267",
  card_token: "gc_abc123def456",
  status: "Cancelled"
)

```

**`CancelGhostCard`**

```csharp CancelGhostCard
using PayabliApi;
using System.Threading.Tasks;

public partial class Examples
{
    public async Task Example() {
        var client = new PayabliApiClient(
            clientId: "YOUR_CLIENT_ID",
            clientSecret: "YOUR_CLIENT_SECRET"
        );

        await client.GhostCard.UpdateCardAsync(
            entry: "8cfec329267",
            request: new UpdateCardRequestBody {
                CardToken = "gc_abc123def456",
                Status = CardStatus.Cancelled
            }
        );
    }

}

```

**`CancelGhostCard`**

```go CancelGhostCard
package example

import (
    context "context"

    payabli "github.com/payabli/sdk-go"
    client "github.com/payabli/sdk-go/client"
    option "github.com/payabli/sdk-go/option"
)

func do() {
    client := client.NewClient(
        option.WithClientCredentials(
            "YOUR_CLIENT_ID",
            "YOUR_CLIENT_SECRET",
        ),
    )
    request := &payabli.UpdateCardRequestBody{
        CardToken: "gc_abc123def456",
        Status: payabli.CardStatusCancelled.Ptr(),
    }
    client.GhostCard.UpdateCard(
        context.TODO(),
        "8cfec329267",
        request,
    )
}

```

**`CancelGhostCard`**

```php CancelGhostCard
<?php

namespace Example;

use Payabli\PayabliClient;
use Payabli\GhostCard\Requests\UpdateCardRequestBody;
use Payabli\Types\CardStatus;

$client = new PayabliClient(
    clientId: 'YOUR_CLIENT_ID',
    clientSecret: 'YOUR_CLIENT_SECRET',
);
$client->ghostCard->updateCard(
    '8cfec329267',
    new UpdateCardRequestBody([
        'cardToken' => 'gc_abc123def456',
        'status' => CardStatus::Cancelled->value,
    ]),
);

```

**`CancelGhostCard`**

```swift CancelGhostCard
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "cardToken": "gc_abc123def456",
  "status": "Cancelled"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.payabli.com/api/MoneyOutCard/card/8cfec329267")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

A successful response confirms the status change.

### Response (200)

```json
{
  "responseText": "Success",
  "isSuccess": true
}
```

## Related resources

See these related resources to help you get the most out of Payabli.

#### Prerequisites

* **[Cards overview](/guides/pay-out-cards-overview)** - Understand how ghost cards and single-use virtual cards differ

#### Related topics

* **[Manage virtual cards with the API](/guides/pay-out-developer-virtual-cards-manage)** - Manage single-use virtual cards instead
* **[Manage payouts with the API](/guides/pay-out-developer-payouts-manage)** - Learn how to use the Payabli API to create, capture, and cancel payout transactions