API versionv2 next

API v2 is not live yet: it launches in early 2027, and until then these pages are its spec. Switch to v1 (live)

gjold API

An HTTPS and JSON API for card payments: create payment links, capture, refund, and void the transactions they create, and charge payers' saved cards.

One rule shapes it: requests change objects, and only synchronization tells you what they became. When a payer pays, or you capture, we send you a ping, and you read the changed objects with seq. A request that changes an object answers with little more than whether it succeeded.

Coming from v1? Moving from v1 to v2 says what changes.

Requests

All requests go to https://api.gjold.com/v2/, or https://api.gjold.dev/v2/ in the test environment, followed by the object's URI, as URLs describes.

Request What it does
POST {shopid}:pay/new Create payment link: a link to the payment window, for one payment.
DELETE {shopid}:pay:{payid} Delete payment link, so that it can't be paid.
POST {shopid}:trn:{trnid}/capture Capture transaction: take some or all of what was authorized.
POST {shopid}:trn:{trnid}/refund Refund transaction: give back some or all of what was captured.
POST {shopid}:trn:{trnid}/void Void transaction: release the authorization, before any capture.
POST {shopid}:sub/new Create subscriber: a link where the payer saves their card.
POST {shopid}:sub:{subid}/charge Charge subscriber without the payer.
POST {shopid}:sub:{subid}/renew Renew subscriber: a link where the payer saves a new card.
DELETE {shopid}:sub:{subid} Delete subscriber, so that it can't be charged.
GET {shopid}:seq:{seq} Seq: the objects that changed since {seq}.
POST to your URL Ping, which we send you when there are changes.

Protocol and Limits, below, are what every request shares, each with a link to the page that has the details, and the implementation notes say how to build an integration that holds up under load and failure.

Protocol

What Value
Production https://api.gjold.com/v2/
Test environment https://api.gjold.dev/v2/, with its own accounts and keys. See Environments.
Transport HTTPS only. Every response has Strict-Transport-Security: max-age=63072000.
Authentication Authorization: Basic with an API key. See Authentication.
URL /v2/{shopid}:{db}, /v2/{shopid}:{db}:{id}, or either followed by /{action}. See URLs.
Request body One JSON object, in UTF-8. See Request bodies.
Success 200, with a JSON object, application/json.
Error A 4xx or 5xx status, with a one-line message in text/plain. See Errors.
Caching Every response has Cache-Control: no-store.
Amounts A decimal string and a currency code, as in "99.95 DKK". See Amounts.
Retries Capture, refund, void, and charge take an Idempotency-Key. See Idempotency.
Waiting Capture, refund, void, and charge wait for the acquirer, for up to 10 minutes per call. See Timeouts.
Changes You learn of every change by ping, and read it with seq. See Synchronization.

Getting started

Follow one payment through the API with curl: from the link that starts it, to its capture. You need an account in the test environment. Its API key and shop ID replace {apikey} and {shopid} below.

Create a link for the amount to pay. -u gives curl your API key to authenticate with, and -d the body, with your order ID and the items:

curl https://api.gjold.dev/v2/{shopid}:pay/new \
    -u {apikey} \
    -d '{"orderid":"1234zx","items":[{"total":"99.95 DKK"}]}'

We answer with the link's "url", and its "id", by which you could delete it:

{
  "url": "https://pay.gjold.dev/q3z3amhrkmyrgk6i",
  "id": "q3z3amhrkmyrgk6i"
}

2. Pay it

Open the "url" in a browser, as a payer would after you redirect them, and pay. Once the payer's bank has authorized the payment, it is a transaction.

3. Read the transaction

In production, we would now ping you. Either way, read the new changes with seq. Your first call starts at 0:

curl https://api.gjold.dev/v2/{shopid}:seq:0 \
    -u {apikey}
{
  "changes": [
    {
      "type": "transaction",
      "id": 1,
      "rev": 1,
      "locked": null,
      "method": {
        "type": "card",
        "id": "JlVFYo77jukOmuAmy6PavAjClA61pM0ydR2rAhCs7lw=",
        "card": {
          "brand": "visa",
          "last4": "1111"
        }
      },
      "services": [
        "clearhaus-1"
      ],
      "info": {
        "ip": "198.51.100.2",
        "ndisplay": 1,
        "nfail": 0
      },
      "time": {
        "created": 1761139386,
        "authorized": 1761139415
      },
      "orderid": "1234zx",
      "billing": {
        "name": "",
        "company": "",
        "vatin": "",
        "gln": "",
        "email": "",
        "phone": "",
        "address": [],
        "city": "",
        "zip": "",
        "state": "",
        "country": ""
      },
      "shipping": {
        "name": "",
        "company": "",
        "email": "",
        "phone": "",
        "address": [],
        "city": "",
        "zip": "",
        "state": "",
        "country": ""
      },
      "items": [
        {
          "quantity": 1,
          "total": "99.95 DKK",
          "name": "",
          "sku": ""
        }
      ],
      "acts": [],
      "totals": {
        "attempted": "99.95 DKK",
        "authorized": "99.95 DKK",
        "captured": "0 DKK",
        "refunded": "0 DKK",
        "voided": "0 DKK",
        "left": "99.95 DKK"
      }
    }
  ],
  "seq": 1
}

The one change is the new transaction, with your "orderid". You need its "id", to capture it, and the "seq" at the end, where your next call to seq starts.

4. Capture it

Capture the amount, as you would when you ship the order. The "index" is the number of acts that the transaction has, 0 so far, which keeps the same capture from going through twice:

curl https://api.gjold.dev/v2/{shopid}:trn:1/capture \
    -u {apikey} \
    -H 'Idempotency-Key: 6f1c2a9e0b7d4c3f8e5a1b2c3d4e5f60' \
    -d '{"total":"99.95 DKK","index":0}'
{}

The empty object says that the capture succeeded, and nothing more.

5. Read the change

Call seq again, from the "seq" that the last call returned, 1:

curl https://api.gjold.dev/v2/{shopid}:seq:1 \
    -u {apikey}
{
  "changes": [
    {
      "type": "transaction",
      "id": 1,
      "rev": 2,
      "locked": null,
      "method": {
        "type": "card",
        "id": "JlVFYo77jukOmuAmy6PavAjClA61pM0ydR2rAhCs7lw=",
        "card": {
          "brand": "visa",
          "last4": "1111"
        }
      },
      "services": [
        "clearhaus-1"
      ],
      "info": {
        "ip": "198.51.100.2",
        "ndisplay": 1,
        "nfail": 0
      },
      "time": {
        "created": 1761139386,
        "authorized": 1761139415
      },
      "orderid": "1234zx",
      "billing": {
        "name": "",
        "company": "",
        "vatin": "",
        "gln": "",
        "email": "",
        "phone": "",
        "address": [],
        "city": "",
        "zip": "",
        "state": "",
        "country": ""
      },
      "shipping": {
        "name": "",
        "company": "",
        "email": "",
        "phone": "",
        "address": [],
        "city": "",
        "zip": "",
        "state": "",
        "country": ""
      },
      "items": [
        {
          "quantity": 1,
          "total": "99.95 DKK",
          "name": "",
          "sku": ""
        }
      ],
      "acts": [
        {
          "act": "capture",
          "time": 1761139447,
          "who": "s1379#1",
          "total": "99.95 DKK",
          "history": []
        }
      ],
      "totals": {
        "attempted": "99.95 DKK",
        "authorized": "99.95 DKK",
        "captured": "99.95 DKK",
        "refunded": "0 DKK",
        "voided": "0 DKK",
        "left": "0 DKK"
      }
    }
  ],
  "seq": 2
}

Seq returns the transaction whole, as it is now. Its "rev" went from 1 to 2, so a copy of it in your database can't overwrite this one: the higher revision wins. The capture is in "acts", with when it happened and which key made it, and "left" shows that nothing more can be captured.

6. Go live

In production, you don't call seq by hand. Give your API key a ping URL, and when a ping comes, call seq until it returns no more changes. Synchronization shows the loop, and the implementation note on it how to run it safely. When your integration works, use your production account's shop ID and API key, at api.gjold.com.

Limits

What Limit When exceeded
Request line and headers 4 KiB together, at most 64 headers 413
Path 8 segments 414
Request body 32 KiB 413
Request body, time to send 30 seconds after the headers 408
First request on a connection 60 seconds after connecting The connection is closed.
Idle keep-alive connection 300 seconds The connection is closed.
Idempotency-Key Kept for 24 hours A later request with the key runs anew.
Seq response 64 KiB, or one change if that is larger The rest comes in the next call.
Ping, your answer 7 seconds to its first byte We hang up, and ping again with the next change or in 5 minutes.

Conventions in these docs

  • {shopid} and the like are placeholders, to replace with a value, braces and all.
  • A JSON field is written in quotes, as "orderid", and a field within another by its path, as items[].total or billing.address[].
  • A field's type is that of JSON, but for amount, which is a string in the amount format, and integer, which is a number without a fraction or exponent.
  • "Required" means that a request without the field fails. Any field not marked so can be left out, and an empty string is the same as leaving it out.

Beyond the API

How card payments work, from authorizations and their time limits to 3-D Secure and the rules for charging saved cards, is in the knowledge base. The API docs link to it where those rules decide what your code must do.