Debugging integrations

Tools and techniques to debug your VINR integration.

View as MarkdownInstall skills

When a charge fails, a webhook never lands, or a subscription drifts out of sync, the fastest path to a fix is reading what VINR actually saw. This page covers the request log, webhook delivery inspection, idempotency replays, and a repeatable sandbox workflow so you can reproduce and resolve issues without guessing.

Start with the request IDAsk

Every API response carries a request ID in the x-vinr-request-id header, and every error body echoes it. Capture and log it on the client side — it is the single fastest way to find the corresponding entry in the Dashboard or to hand to support.

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

try {
  const payment = await vinr.payments.create({
    amount: 1000,
    currency: 'EUR',
    customer: 'cust_8sQ2',
  });
  console.log('ok', payment.id);
} catch (err) {
  // VinrError exposes the request ID, HTTP status, and a stable code.
  console.error('vinr error', {
    requestId: err.requestId,
    status: err.status,
    code: err.code,        // e.g. card_declined, idempotency_conflict
    message: err.message,
  });
}

Codes (err.code) are stable across versions and safe to branch on. The human-readable message is for logs and is not a contract — never parse it.

Request logsAsk

The Dashboard logs every API request against your account: method, path, status, latency, the request ID, and the redacted request/response bodies. Filter by status code to surface failures, or paste a request ID straight into the search box.

You can also pull the same data programmatically to wire it into your own observability:

curl https://api.vinr.com/v1/request_logs?status=4xx \
  -H "X-Api-Key: $VINR_SECRET_KEY"
FilterUse it to find
status=4xxBad input, auth failures, idempotency conflicts
status=5xxTransient VINR-side errors worth retrying
resource=paymentEvery call touching a specific resource type
request_id=req_…A single exact request

Logs in the live account are retained for 30 days; sandbox logs for 7. Export anything you need to keep beyond that window.

Inspecting webhook deliveryAsk

Most "the event never arrived" reports are delivery failures you can see and replay. Open the webhook endpoint in the Dashboard to view every attempt, the HTTP status your server returned, the response body, and the next scheduled retry.

Confirm the event was generated

Find the resource (for example pay_…) in its log and check that the expected transition happened at all. A payment stuck in requires_authentication never fires payment.completed.

Check the delivery attempts

A non-2xx response or a timeout (over 10 seconds) counts as a failure. VINR retries with exponential backoff for up to 72 hours.

Verify the signature locally

A silent 400 from your own handler is the most common cause. Confirm you pass the raw request body — not a parsed object — to the verifier.

Replay the delivery

Once your endpoint is fixed, click Resend on any past attempt, or replay from code (see below).

// The body MUST be the raw bytes. Re-serializing JSON changes the
// signature and verification will fail.
export async function POST(req: Request) {
  try {
    const event = vinr.webhooks.verify(
      await req.text(),
      req.headers.get('x-vinr-signature'),
    );
    console.log('verified', event.id, event.type);
    return new Response('OK', { status: 200 });
  } catch (err) {
    console.error('signature verification failed', err.message);
    return new Response('bad signature', { status: 400 });
  }
}

Idempotency replaysAsk

When you send an idempotency key, VINR returns the original stored response for any retry with the same key. That makes debugging deterministic: replay the exact request and compare. A response carrying x-vinr-idempotent-replay: true means you hit the cache, not a fresh execution.

curl https://sandbox.api.vinr.com/v1/payments \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Idempotency-Key: order-4711-attempt-1" \
  -H "Content-Type: application/json" \
  -d '{"amount":1000,"currency":"EUR","customer":"cust_8sQ2"}'

An idempotency_conflict error means you reused a key with a different body. Generate a fresh key per logical operation; do not recycle keys across distinct requests.

Common pitfallsAsk

Reproducing in sandboxAsk

The sandbox mirrors live behavior with deterministic test cards, so you can trigger any branch on demand.

Card numberOutcome
4242 4242 4242 4242Succeeds → payment.completed
4000 0000 0000 0002Declined → payment.failed (card_declined)
4000 0000 0000 3220Triggers 3DS → payment.requires_authentication

To debug webhooks without a public URL, run the CLI to forward live deliveries to localhost:

vinr listen --forward-to http://localhost:3000/api/webhooks

The CLI prints the signing secret for the forwarding session and logs every event it relays, so you can step through handlers in your debugger.

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page