# Quickstart: Direct API

Authenticate with an API key and take your first card payment over the Syntch API.

**Category:** Quickstart

**Last reviewed:** 15 August 2026

# Quickstart: Direct API

Your server holds the card details and posts them to Syntch. This is the shortest path if you already handle card data under your own PCI scope, or if you are charging a card the payer isn't present for.

You need two things: the base address of your Syntch deployment, and an API key. Both come from your integration contact if you don't have them yet.

Every request below sends the key in an `api-key` header. No login call, and no token to refresh.

## 1. Prove the key works

Ask for the transaction list. An empty `items` array is a success: it means the key authenticated and the account simply has nothing in it yet.

```bash
curl "https://your-gateway-host/api/transactions" \
  -H "api-key: YOUR_API_KEY"
```

```json
{
  "items": [],
  "totalCount": 0
}
```

A key that's not accepted comes back as HTTP 401 with a stable `code` you can branch on:

```json
{
  "error": "Unauthorized",
  "code": "KEY_INVALID",
  "message": "API key is not valid."
}
```

None of the 401 codes is transient, so don't retry a refused key. Log the `code` and never the key itself.

## 2. Take a payment

One request creates the transaction and runs it. `4111111111111111` is the network test number and is the only card number that belongs in a code sample.

```bash
curl -X POST "https://your-gateway-host/api/transactions" \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionType": "Sale",
    "idempotencyKey": "quickstart-0001",
    "cardData": {
      "cardNumber": "4111111111111111",
      "nameOnCard": "Jane Doe",
      "expirationMonth": 12,
      "expirationYear": 2030
    },
    "invoiceData": {
      "amounts": { "base": 10.00, "total": 10.00 }
    }
  }'
```

The same call in C#:

```csharp
using var http = new HttpClient { BaseAddress = new Uri("https://your-gateway-host") };
http.DefaultRequestHeaders.Add("api-key", "YOUR_API_KEY");

var response = await http.PostAsJsonAsync("/api/transactions", new
{
    transactionType = "Sale",
    // Reproduce this value on a retry so a timed-out request replays instead of charging twice.
    idempotencyKey = "quickstart-0001",
    cardData = new
    {
        cardNumber = "4111111111111111",
        nameOnCard = "Jane Doe",
        expirationMonth = 12,
        expirationYear = 2030
    },
    invoiceData = new
    {
        amounts = new { @base = 10.00m, total = 10.00m }
    }
});

response.EnsureSuccessStatusCode();

var created = await response.Content.ReadFromJsonAsync<JsonElement>();
var transactionId = created.GetProperty("id").GetString();
```

The response is the created transaction. The two properties worth reading first are its `id`, which every later call addresses it by, and the outcome under `responseData`:

```json
{
  "id": "6f3b2c18-0a4d-4a9e-9d4f-2b71c2f0a911",
  "transactionType": "Sale",
  "invoiceData": {
    "amounts": { "base": 10.00, "total": 10.00 }
  },
  "responseData": {
    "resultCode": "Ok",
    "resultMessage": "APPROVED",
    "authorizationCode": "TST123"
  }
}
```

`resultCode` is the value to branch on. `Ok` is the gateway accepting the request; anything else names what went wrong, and a refusal by the issuer arrives here rather than as a failed HTTP call.

Card data is optional in a sample but not always at the processor: send `cvv` alongside the card fields when you have collected it, since many processors require it for a card-not-present sale. It's absent above by design, because a published example should never teach that echoing or storing the value is normal.

## 3. Read the transaction back

```bash
curl "https://your-gateway-host/api/transactions/{transactionId}" \
  -H "api-key: YOUR_API_KEY"
```

Wire this in from the start rather than trusting the create response alone. A request that times out in transit leaves you with no response and a payment that may well have gone through, and the read is how you find out which.

## 4. Handle the refusal path

A declined payment is a normal outcome, not a transport error: it arrives as a successful HTTP response whose `responseData` carries the refusal. Branch on the result, never on the HTTP status alone.

Your deployment's sandbox refuses specific amounts on purpose so you can exercise that branch before you go live. The testing page on the developer documentation site lists the amounts your environment reacts to, and it reads them from the simulator rather than restating them, so they can't drift.

## Next steps

- [Getting started with the API](/help/guides/api-getting-started) covers idempotency, rate limits, timestamps, and what an API key can reach.
- [Webhook integration](/help/guides/webhook-integration) covers receiving the transaction result at your own endpoint instead of polling for it.
- [Reusing a stored payment method with payment tokens](/help/guides/reusing-saved-cards) covers charging a stored card again without holding the number.

## You need credentials to run this

Every request on this page sends an API key. Ask your Syntch contact for access to a sandbox merchant, then create a key against it in the application. A key is shown in full once, when you create it.

This instance publishes no support address. Whoever provisioned your access can issue the sandbox merchant.

## See also

- [All documentation](https://devportal-simpay-sbx.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
