# Create a Transaction

## What is a Transaction?

A **Transaction** in the GrailPay Payments API initiates the movement of funds between two parties — a payer ( sender ) and a
payee ( recipient ) — via the ACH network. It represents the debit leg of the transfer, capturing the withdrawal of funds from the
payer’s bank account. Once processed, the transaction will result in a corresponding **Payout**, which reflects the credit
leg — the delivery of funds to the payee. Each transaction is uniquely identified by a uuid and includes metadata such
as the amount, participants, and processing status.


Transactions can represent:

- **Merchant to Merchant** payments
- **Merchant to Business** or **Business to Merchant** payments
- **Merchant to Person** disbursements
- **Person to Merchant** collections
- **Person to Person** payments

Once created, the transaction enters a processing pipeline that will debit the payer's bank account and credit the
payee's bank account, subject to ACH network rules and timing.

---

## Overview

Creating a transaction involves initiating a fund transfer between two parties — a **payer** (sender) and
a **payee** (recipient). Both parties must be onboarded and must have valid, connected bank accounts within the
GrailPay system.

When a transaction is successfully created, the API returns a unique `uuid` that can be used to track its status or
retrieve its details later.

This guide outlines the required fields, entity prerequisites, and common validation scenarios encountered when
initiating a transaction.

---

## Person to Person Payments

GrailPay supports payments directly between two people, allowing funds to move from one individual to another without a
registered Merchant on either side of the transaction.

Please note that fulfilling the compliance obligations applicable to ACH operates differently for Person to Person
payments than payments with Merchants in our system.

To create a Person to Person transaction, both of the following must be true:

- **The payee's KYC status is `approved`.** The individual receiving funds must have successfully passed Know Your
Customer (KYC) identity verification and reached the `approved` status before you create the transaction. Submitting a
person for KYC does not immediately approve them — wait for the
[`compliance_status.approved`](/docs/technical/webhooks/events/compliance-status/#compliance_statusapproved) webhook event. A `submitted`, `rejected`, or any `ON_HOLD_*` status does **not** satisfy
this requirement. See [Submit Person for KYC](/docs/technical/users/manage-users#submit-person-for-kyc) to initiate
verification for a person.
- **Your account is approved for Person to Person payments.** This functionality must be explicitly enabled on your
integration by GrailPay.

:::note
Person to Person payments are not enabled by default. If you would like to offer this functionality, contact your
GrailPay integration support contact to have it enabled on your account.
:::

:::caution
A Person to Person transaction will be rejected if the payee's KYC status is not `approved`, or if your account is
not approved for Person to Person payments.

Before creating the transaction, wait for the payee's KYC to reach an `approved` status, delivered via the
[`compliance_status.approved`](/docs/technical/webhooks/events/compliance-status/#compliance_statusapproved) webhook event. Any other
status — including `submitted`, `rejected`, or any `ON_HOLD_*` status — does not satisfy this requirement.

All other transaction types continue to require at least one KYB-verified Merchant, as described above.
:::

---

## Destination Account & Batch Payouts

When creating a transaction, you can optionally specify a destination account — the payee's bank account that will
receive the payout — using the `destination_bank_account_uuid` field. Omit the key to use the payee's default connected
account; do not send `null`. The payer side works the same way: `source_bank_account_uuid` selects the account to
debit, and omitting it uses the payer's default connected account. How GrailPay uses the destination value depends on
whether the merchant receiving the funds is configured for **Batch Payouts**, that is, whether its `payout_type` is `batch`.

| Merchant configuration               | Where the payout is sent                                                                                            |
|--------------------------------------|---------------------------------------------------------------------------------------------------------------------|
| **Not** configured for Batch Payouts (`payout_type` is `individual`) | The destination account specified during transaction creation (`destination_bank_account_uuid`).                    |
| Configured for Batch Payouts (`payout_type` is `batch`)         | The merchant's **default bank account**. The destination account specified during transaction creation is not used. |

:::note
This is expected system behavior. For merchants configured for Batch Payouts, payouts are **always** sent to the
merchant's default bank account, regardless of any destination account provided at transaction creation. The
destination account you specify is only used when the merchant is **not** configured for Batch Payouts.
:::

For more on how batch and direct payouts are delivered, see
[Scenarios: Direct & Batch Payouts](/docs/resources/scenarios-direct-and-batch-payouts).

To update a Merchant or Business to receive individual payouts instead of Batch Payouts (its `payout_type`), please refer to
the [Manage Users](/docs/technical/users/manage-users) page.

---

## Transaction Metadata

When creating a transaction, you can optionally include metadata fields that describe the transfer. These values are
passed through to the ACH entries and appear on bank statements and in ACH records, helping both the payer and payee
recognize the purpose of the payment.

| Field          | Max Length    | Description                                                                                                                     |
|----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------|
| `company_name` | 16 characters | The originator (company) name displayed on the ACH entry and bank statement. Auto-populated when omitted — see the rules below. |
| `description`  | 10 characters | A short descriptor of the transfer's purpose included on the ACH entry.                                                         |
| `addenda`      | 80 characters | Supplemental free-form information attached to the ACH entry. Only transmitted on CCD (business-to-business) entries.           |

All three fields are optional. Any value you send is sanitized before it is used:

- Only letters, numbers, and spaces are kept — any other characters are removed.
- Consecutive spaces are collapsed into a single space.

:::caution
These fields are **not** truncated. If a value exceeds its maximum length, the API rejects the entire transaction
request rather than trimming the value. Check the length of `company_name`, `description`, and `addenda` before
submitting.

For clean, predictable metadata, send values that are already within the character limits and avoid punctuation or
symbols you need preserved.
:::

### Capture vs. Payout Metadata

Every transfer moves money in two legs, and metadata is applied **independently** to each:

- **Capture** — the debit that pulls funds from the payer's bank account. Uses the values you send at creation.
- **Payout** — the credit that delivers funds to the payee's bank account. Derives its own metadata later, drawing on
  the stored transaction values and the payer's profile.

Because the two legs are computed separately, they can display different values, and leaving a field blank at creation
does **not** mean it will be empty on the payout.

### Capture metadata (debit leg)

At creation, the values you provide are applied to the capture ACH entry:

| Field          | When you provide a value            | When you omit it                                                                                                                        |
|----------------|-------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| `company_name` | Used as sent, after sanitization.   | Auto-generated from the **payee**: the payee's business/merchant name, otherwise the payee's first and last name, otherwise left empty. |
| `description`  | Used as sent, after sanitization.   | Left empty on the capture (no fallback at this stage).                                                                                  |
| `addenda`      | Stored as sent, after sanitization. | Left empty.                                                                                                                             |

:::note
`addenda` is always stored on the transaction, but it is only transmitted on the outgoing ACH entry for CCD
(business-to-business) entries. On PPD or WEB entries the value is retained but not sent to the bank.
:::

### Payout metadata (credit leg)

When the transaction reaches payout, metadata for the credit ACH entry is recalculated — it does not simply copy the
capture values. Each field falls through the following order until a usable value is found. Because these payout values
are derived by GrailPay rather than submitted in your request, they are sanitized and trimmed to fit each field's limit:

| Field          | Fallback order (first match wins)                                                                                                                 |
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
| `company_name` | 1) the transaction's `company_name` → 2) the payer's business/merchant name → 3) the payer's name → 4) `GrailPay` (default)                       |
| `description`  | 1) the transaction's `description` → 2) the payer's business/merchant name → 3) the payer's name → 4) empty                                       |
| `addenda`      | Included only when the payout is sent as CCD (business-to-business), using the transaction's `addenda`. Omitted (`null`) for PPD and WEB payouts. |

:::note
Because the payout fills in `company_name` and `description` from the payer's profile when you leave them blank, the
credit entry is rarely empty even if you send no metadata. If you want consistent, predictable values on both legs,
send `company_name` and `description` explicitly.
:::

:::note
**Batch Payouts only:** GrailPay offers an optional, account-level configuration — available on request — that
overrides the `company_name` fallback order above and always sends your (the client's) name as the originator
company name on batch payout entries. Contact your GrailPay integration support contact to enable it.
:::

---

## Testing

To test transactions in GrailPay’s sandbox environment, you’ll need two entities:
- One with a connected bank account to act as the payer
- One with a connected bank account to act as the payee

For more information on creating accounts in our sandbox environment, see the [Technical Overview](/docs/technical/overview#testing) guide.

You can provide these details when creating test users via the following endpoints:
- [Onboard a Person](/docs/technical/users/onboard-a-person)
- [Onboard a Business](/docs/technical/users/onboard-a-business)
- [Onboard a Merchant](/docs/technical/users/onboard-a-merchant)
- [Add Bank Account](/docs/technical/bank-accounts/add-bank-account)

This setup allows you to perform end-to-end transaction testing — including creation, settlement, and simulated
failure — without using real financial data.

:::note
Most transactions require that at least one party (either the payer or payee) has successfully completed KYB
verification — meaning one side must be a registered Merchant. The exception is Person to Person payments, which
instead require the payee to have completed KYC verification and your account to be approved for the functionality
(see [Person to Person Payments](#person-to-person-payments) above).
:::

---

## Step 1: Confirm Participants Are Onboarded

Before creating a transaction, both the **payer** and **payee** must exist in the system as valid entities under your
vendor — a person, a business, or a merchant. You reference each by the UUID you were given when it was onboarded; there is no
separate user UUID to look up.

Each must meet the following criteria:

- Successfully onboarded using the appropriate flow
- Possess a valid `uuid`
- Have an active, connected bank account (manual or Plaid-linked)

You will reference these participants using:

- `payor_uuid` — the sender of funds
- `payee_uuid` — the recipient of funds

If either party is missing or has an invalid account, the transaction will be rejected.

---

## Step 2: Determine Transaction Type and Amount

Each transaction must include:

- `payor_uuid` and `payee_uuid` — The two participants, as described in Step 1
- `amount` — The value to transfer, in cents

Optional fields include:

- `transaction_fee` — A fee in cents, which must not exceed `amount`
- `client_reference_id` — A reference ID from your system
- `company_name`, `description`, and `addenda` — ACH metadata, as described under
[Transaction Metadata](#transaction-metadata)
- `source_bank_account_uuid` and `destination_bank_account_uuid` — The accounts to debit and credit, as described
under [Destination Account & Batch Payouts](#destination-account--batch-payouts)
- `modality` — The rail and speed of each leg. `modality.transaction` covers the debit and is ACH only, with a
`speed` of `standard` or `sameday`. `modality.payout` covers the credit: `payment_rail` is `ach` or `fednow`
where FedNow is enabled for your account, and `speed` is `standard`, `sameday`, or `instant`, with `instant`
valid only on `fednow`. When `modality.payout` is omitted, GrailPay resolves a default from your vendor and the
payee's configuration. It is rejected when your account uses processor batch payouts.

All amounts must be denominated in USD and specified in cents (e.g., $10.00 = 1000). The platform minimum is 10 cents,
and a higher minimum may apply if one is configured for your account.

---

## Step 3: Submit the Transaction

To initiate the transaction, send a `POST` request to `/api/v3/transactions` with the required fields. Include an
[`Idempotency-Key` header](/docs/technical/idempotency) so that a retried request cannot create a second transaction;
a duplicate is answered with `409`. Upon success, the API returns `201` with the new transaction, including the
`uuid` that identifies it from here on and an initial status of `QUEUED`. The `uuid` is what you use to fetch
status updates, pause or resume the transaction, or cancel it.

### Character Limits

When creating a new transaction, certain fields are subject to strict character limits. These constraints are imposed by
the ACH network and our banking partners to ensure compatibility with downstream systems:

| Field          | Character Limit |
|----------------|-----------------|
| `company_name` | 16 characters   |
| `description`  | 10 characters   |
| `addenda`      | 80 characters   |

Make sure your values do not exceed these limits, or the transaction may be rejected during processing.


See the [Create Transaction OpenAPI documentation](/api/payments/#tag/transactions/POST/api/v3/transactions)
for the full schema, including all required and optional fields.

---

## Step 4: Monitor the Transaction Status

Once submitted, a transaction may move through multiple states depending on processing speed, ACH timing, and risk evaluation.

Possible states include:

- `QUEUED`
- `CAPTURE_PENDING`
- `CAPTURE_ACH_PENDING`
- `CAPTURE_ACH_FAILED`
- `CANCELED`
- `IN_PAYOUT`

To see a full list of states and their definitions, refer to the [Transaction States](/docs/resources/transaction-states) documentation.

To monitor the state of a transaction, you can use one of the following methods:
- **Webhook Notifications**: Register a webhook to receive the
[Transaction events](/docs/technical/webhooks/events/transaction/) — `transaction.processing`,
`transaction.completed`, `transaction.failed`, and the rest — as the status changes. See
[Webhook Events](/docs/technical/webhooks/events/) for the envelope every event shares.
- **API Polling**: Use the `GET /api/v3/transactions/{uuid}` endpoint to fetch the current state of a specific
transaction, along with its payout, refunds, and clawback.

:::note
To fully monitor the lifecycle of a funds transfer, it’s important to understand that a Transaction only represents
the debit leg of the movement. The Payout, which delivers funds to the payee, follows its own processing lifecycle
and status updates.

We **strongly recommend** handling both the `transaction.*` and
[`payout.*`](/docs/technical/webhooks/events/payout/) webhook events to accurately track the complete flow of funds —
from initiation to settlement. A single v3 webhook registration receives both.
:::

---

## Questions?

If you're encountering any issues, contact your GrailPay integration support contact or open a support request via the portal.

---

## Resources

Below you will find some helpful resources to assist with understanding how transactions work in GrailPay.

- [Transaction States](/docs/resources/transaction-states)
- [ACH Return Codes](/docs/resources/return-codes)
- [Scenarios: Direct & Batch Payouts](/docs/resources/scenarios-direct-and-batch-payouts)
- [Transaction Queues](/docs/resources/transaction-queues)