Build with theShipToVerified REST API
Create verification sessions, embed our widget on your checkout, and listen for signed webhooks. Everything you need to ship a verified order.
Base URL https://api.shiptoverified.com
Developer-First Design
A small, predictable surface: REST + JSON, signed webhooks, and an embeddable widget.
RESTful Design
Clean, predictable URLs and standard HTTP methods. JSON in, JSON out.
Signed Webhooks
Real-time notifications with HMAC-SHA256 signatures and automatic retries with exponential backoff.
Embedded Widget
Ship a single JavaScript snippet and let your customers complete ID capture without leaving checkout.
Free 14-Day Trial
Every account starts with a 14-day free trial — verify real orders end-to-end before you commit.
Authentication
Every request to the public API requires two headers issued from your dashboard. The one exception is POST /api/v1/checkout/eligibility, which authenticates with the API key alone so a storefront can call it.
Required headers
X-API-Key— your merchant API key (e.g.vfy_live_xxxxx).X-API-Secret— the paired secret. Treat like a password; never expose it client-side.
curl https://api.shiptoverified.com/api/v1/merchants/me \
-H "X-API-Key: vfy_live_xxxxxxxxxxxxx" \
-H "X-API-Secret: YOUR_API_SECRET"Rotate compromised credentials from your dashboard, under Settings → Account → API keys. The previous pair is invalidated immediately.
Verification Endpoints
The core of the integration: create a verification, then drive it to completion.
/api/v1/verificationsCreate a verification session for a customer order. The response includes a short-lived widget_token you can hand to the embedded widget on your storefront, and a verification_url you can send to the customer instead. Idempotent on transaction_id — a duplicate POST returns the existing verification (200) with a fresh token. Omit transaction_id to create a standalone verification with no order behind it.
// Request body
{
"transaction_id": "ORD-12345",
"customer_email": "customer@example.com",
"target_name": "Jane Q. Buyer",
"target_address": {
"line1": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"postal_code": "90001",
"country": "US"
}
}
// 201 Created
{
"id": "ver_abc123",
"transaction_id": "ORD-12345",
"status": "pending",
"widget_token": "eyJhbGci...",
"verification_url": "https://app.shiptoverified.com/verify/ver_abc123?token=eyJhbGci...",
"created_at": "2025-01-15T18:04:11Z"
}/api/v1/verifications/{id}Retrieve the current state of a verification. Prefer webhooks over polling for completion notifications.
// 200 OK
{
"id": "ver_abc123",
"status": "verified",
"name_match": { "is_match": true, "score": 0.95, "method": "fallback" },
"address_match": { "is_match": true, "score": 0.92, "method": "fallback" },
"verified_name": "Jane Q. Buyer",
"verified_at": "2025-01-15T18:07:42Z"
}/api/v1/verifications/{id}/processSubmit the front of the customer’s ID document for OCR and matching. Supply either image_base64 or image_url.
// Request body — raw base64 only, no data: URI prefix
{
"image_base64": "/9j/4AAQSkZJRg..."
}/api/v1/verifications/{id}/secondary-proofSubmit a secondary proof of address (utility bill, lease, bank statement). Only valid when requires_secondary_proof is true.
/api/v1/verifications/{id}/use-id-nameResolve a name mismatch by accepting the name extracted from the ID document.
/api/v1/verifications/{id}/use-id-addressResolve an address mismatch by accepting the address extracted from the ID document.
/api/v1/verifications/{id}/acknowledgeFinalize a verification once name and address are resolved. Fires the verification.completed webhook and marks the verification verified.
/api/v1/verifications/{id}Cancel a verification that has not completed (a verified one is refused with 400). The widget token can no longer be used to submit documents; fires the verification.cancelled webhook.
Merchant Endpoints
Read your merchant profile and manage settings programmatically. API credentials are rotated from the dashboard.
/api/v1/merchants/meReturn the merchant account that owns the API credentials in this request.
/api/v1/merchants/me/settingsRead non-sensitive merchant settings (branding, verification rules, notifications).
/api/v1/merchants/me/settingsUpdate merchant settings. Only fields included in the body are changed.
The public API also covers verification rules, exemptions, requesting secondary proof, checkout eligibility quotes, and fee reporting — see the full OpenAPI reference for every endpoint.
Verification Status Lifecycle
Every verification moves through a deterministic set of states.
pendingVerification created, awaiting an ID submission from the customer.
processingAn ID has been submitted and is being analyzed.
awaiting_actionA name/address mismatch needs to be resolved (use ID data, request secondary proof, etc.).
verifiedVerification complete and acknowledged.
failedDocument could not be processed or verification could not be completed.
cancelledVerification was cancelled by the merchant.
Webhooks
Set a webhook_url via PATCH /api/v1/merchants/me/settings and we'll POST signed events to it as verifications progress.
Events
verification.createdEmitted right after a verification is created — via the API, the dashboard, or a store integration when an order triggers verification.verification.completedEmitted when the verification becomes verified — on acknowledgment, or automatically for a returning customer. Carries verified_name, verified_address, and updated_fields (the order fields the customer replaced with their ID values).verification.failedEmitted when document processing ends in a terminal failure (e.g. the age check).verification.cancelledEmitted when a verification is cancelled via DELETE /verifications/{id}, or when its order becomes exempt after the fact.
Headers we send
X-VerifyID-Event— event name (e.g.verification.completed).X-VerifyID-Signature—t=<ts>,v1=<hmac_sha256(ts.payload, secret)>.User-Agent—VerifyID-Webhook/1.0.
Example payload
{
"event": "verification.completed",
"timestamp": "2025-01-15T18:07:42+00:00",
"verification_id": "ver_abc123",
"transaction_id": "ORD-12345",
"source": "order",
"customer_id": "cus_4f12",
"status": "verified",
"verified_name": "Jane Q. Buyer",
"verified_address": {
"line1": "123 Main St", "city": "Los Angeles",
"state": "CA", "postal_code": "90001", "country": "US"
},
"updated_fields": [],
"verified_at": "2025-01-15T18:07:42+00:00",
"method": "primary_id"
}Delivery & retries
We retry non-2xx responses with backoff at 1 min, 5 min, 15 min, and 1 hr. Respond with any 2xx status within 30 seconds to acknowledge delivery. The signature is an HMAC-SHA256 keyed with the webhook_secret returned by GET /api/v1/merchants/me/settings — always verify it before trusting payload contents.
Widget Events
If you embed the widget without a backend webhook listener, subscribe to the same lifecycle in the browser with STV.on(...). Every detail carries orderId.
Reacting to a result
On verified, the detail includes verifiedName, verifiedAddress, and updatedFields — the order fields the customer replaced with their ID values ("name" and/or "address", or empty). Apply them to your order without diffing what you originally submitted.
<script src="https://api.shiptoverified.com/widget/v1.js"></script>
<script>
STV.on("verified", function (d) {
// d = { orderId, verifiedName, verifiedAddress, updatedFields }
if (d.updatedFields.length) applyToOrder(d.verifiedName, d.verifiedAddress);
releaseOrder();
});
STV.on("failed", function (d) { routeToManualReview(d.reason); });
STV.on("error", function (d) { console.error(d.message); });
STV.init({ widgetToken: "WIDGET_TOKEN", orderId: "ORD-12345", apiUrl: "https://api.shiptoverified.com" });
</script>Available events
mounted— the widget rendered its UI.verified— identity verified; detail carries verifiedName, verifiedAddress, updatedFields.failed— the customer did not pass; detail carries reason.error— a technical failure (expired session, unreadable file); detail carries message.address-review/address-updated/address-unverified— fire only in the standalone address-only (Radar) validation flow, not the ID flow.
Errors
Errors are returned as JSON with a detail field. Conventional HTTP status codes indicate the error class.
400Verification is in a state that does not accept the requested action (e.g. processing an already-verified session).
401Missing or invalid X-API-Key / X-API-Secret headers.
402Your subscription is inactive. Reactivate billing before creating verifications.
404Verification not found for this merchant.
409The order is exempt from verification (you flagged it, or its address is on your exemption list). Nothing was created or charged — treat it as no verification needed.
422Request body failed validation. Check detail for field-level errors.
500Unexpected server error. Safe to retry with exponential backoff.
Quick Start
Create your first verification in three steps.
1. Get your API credentials
Sign up for a free dashboard account; your API key + secret appear under Settings → Account → API keys.
2. Create a verification
curl -X POST https://api.shiptoverified.com/api/v1/verifications \
-H "X-API-Key: vfy_live_xxxxxxxxxxxxx" \
-H "X-API-Secret: YOUR_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"transaction_id": "ORD-12345",
"customer_email": "customer@example.com",
"target_name": "Jane Q. Buyer",
"target_address": {
"line1": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"postal_code": "90001",
"country": "US"
}
}'3. Embed the widget
On your order-confirmation page, load the standalone bundle and hand it the widget_token from step 2 via STV.init(...). The widget walks the customer through ID capture; react to the result with STV.on(...) (see Widget Events) or the verification.completed webhook.
<!-- On your order-confirmation page -->
<div id="shiptoverified-widget"></div>
<script src="https://api.shiptoverified.com/widget/v1.js"></script>
<script>
STV.init({
widgetToken: "WIDGET_TOKEN_FROM_STEP_2",
orderId: "ORD-12345",
apiUrl: "https://api.shiptoverified.com",
});
</script>WooCommerce, BigCommerce, Magento, and Shopify merchants don't need to embed the script manually — install the ShipToVerified extension for your platform and the widget is injected for you.
Ready to Integrate?
Get your API keys and create your first verification today. Contact us if you want help wiring up your integration.