Vendor API · v1

Vendor API v1 reference

The complete contract for issuing and tracking single-use PropFirmConnector discount codes from your own backend — authentication, idempotency, the three REST endpoints, signed webhooks, errors, rate limits, and retention.

API version v1 Last updated 2026-08-17 HTTPS + JSON
On this page
  1. Overview
  2. Environments & transport
  3. Authentication
  4. Idempotency
  5. Identity boundary
  6. Discount codes
  7. The discount_code object
  8. POST /discount-codes
  9. GET /discount-codes/{id}
  10. GET /redemptions
  11. Webhooks
  12. Errors
  13. Rate limits & retries
  14. Data retention & security
  15. Go-live
  16. Versioning
  17. Getting access

Overview#

The Vendor API lets an approved partner (a prop firm or trading-tool vendor) issue single-use PropFirmConnector discount codes to its own customers and track what happens to each code. Your backend calls the API after authenticating your own customer; PropFirmConnector remains the authority for checkout, billing, license entitlement, and code state.

The platform-wide v1 rules:

  • One code purchases one independent PropFirmConnector license slot.
  • The same PropFirmConnector account may redeem more than one code. Codes do not stack on one slot and cannot be combined with another discount.
  • A code can be used for one plan allowed by its immutable server-side offer, not for multiple plans.
  • A customer with no PropFirmConnector trial, license, entitlement, or billing history receives the normal seven-day free trial. Activating that trial consumes the code even if the customer later cancels or never makes a paid payment.
  • A customer with any PropFirmConnector history is charged immediately. This includes an ended or canceled trial, an inactive or reversed entitlement, and a current or prior paid entitlement. The vendor cannot select or override this.
  • Canceling Monthly and later starting a new billing agreement does not preserve a prior vendor offer. A new, unused code is required.

An immutable offer can apply one percentage across its plans or a separate fixed discount amount for each allowed plan. Exact base, discount, and final prices are frozen in a server-owned registry configured during onboarding; requests can never select or override the offer, its pricing, or the tenant they act for. Payment amounts, provider-specific payment details, commissions, fees, payouts, and revenue-share data are never returned through this API or its webhooks.

Environments & transport#

All requests use HTTPS with TLS 1.2 or newer and JSON encoded as UTF-8. The stable resource paths are:

Resource paths
POST /vendor/v1/discount-codes
GET  /vendor/v1/discount-codes/{discount_code_id}
GET  /vendor/v1/redemptions

The production base URL is supplied during onboarding. Every promotion ID, status read, redemption-list row, cursor, and webhook event is bound to the authenticated vendor and the server-selected environment; records from another vendor or environment fail closed.

Requests with a body must send Content-Type: application/json.

Clients must accept additive response fields and new webhook event types. Unknown request fields are rejected so misspellings never silently change behavior. A breaking change requires a new major path such as /vendor/v2.

Authentication#

Each approved vendor receives an opaque bearer API key bound to one environment; production keys begin with pfc_live_. A key has this server-generated shape:

Key format
pfc_{test|live}_{key_id}_{43-character-base64url-secret}

The embedded key ID is opaque and routes verification to one credential record. That stored record is the sole authority for the request's vendor, environment, and scopes — a request body, URL, query parameter, header, promotion ID, or cursor can never select or override the tenant. The complete bearer key is the secret: it is displayed exactly once at creation, PropFirmConnector stores only a one-way verifier, and an existing key can never be retrieved or redisplayed.

Keep the key server-side. The bearer key must live in your server-side secret manager. It must never be embedded in a browser, mobile app, URL, source repository, analytics event, customer-facing page, or client-side storage. Your backend calls this API after authenticating your own customer.

Required headers#

Every request
Authorization: Bearer pfc_live_k0123456789abcdef01234567_<43-character-secret>

Create requests also include Idempotency-Key; authenticated reads do not. Supplying multiple or malformed Authorization values is rejected. Launch keys normally receive these scopes:

  • discount_codes:create for POST /vendor/v1/discount-codes
  • discount_codes:read for GET /vendor/v1/discount-codes/{discount_code_id}
  • redemptions:list for GET /vendor/v1/redemptions

The supplied bearer value is hashed and compared against a fixed-length verifier in constant time. Missing, malformed, unknown, wrong-environment, out-of-scope, expired, and revoked credentials all return the same 401 VENDOR_AUTHENTICATION_FAILED response — the response never reveals which component failed.

Key lifecycle and rotation#

A newly generated key begins as pending and expires if it is not used within 24 hours. Its first valid, in-scope request atomically activates it. Each vendor and environment permits at most two non-expired pending-or-active keys, and at most one pending key, which supports a clean rotation overlap:

  1. Create a replacement key and store its one-time bearer value immediately.
  2. Send one valid, in-scope request with it; the key becomes active.
  3. Confirm successful traffic, then revoke the old key.

If the one-time display is lost, revoke that pending key and create another — PropFirmConnector cannot recover it. A compromised key must be revoked immediately. Revocation is authoritative on the next request and never alters already-created discount codes or entitlements.

API keys and webhook signing secrets rotate independently: the webhook_secret is used only by your receiver to verify callbacks PropFirmConnector sends you, and it never authenticates inbound API requests. Key and webhook management is performed by your enrolled administrators in the vendor portal, which requires authenticator-app (TOTP) sign-in.

Idempotency#

POST /vendor/v1/discount-codes requires an idempotency key:

Header
Idempotency-Key: 92e671d3-7283-4cb8-93fb-1f3fcd8db36c

The value must be 16–128 characters, start with an ASCII letter or digit, and contain only ASCII letters, digits, ., _, :, or -. Use a random UUID generated for one create operation, and never encode customer information into it. Its server-side scope is the authenticated vendor and environment. Idempotency records are retained for 12 months:

  • Repeating the same create with the same key and byte-identical body returns the same code and promotion ID. A new operation returns 201; a replay returns 200 with replayed: true.
  • Reusing the key with a different body returns 409 with error code VENDOR_PROMOTION_CONFLICT and creates no code.
  • Concurrent duplicates serialize to one creation and an idempotent replay.
  • After 12 months the record may be deleted. Never intentionally reuse an old key — a reused key whose record aged out would mint a second code and a second license slot.
Recover lost responses by retrying, never by re-keying. If the network drops after a create request, retry the same body with the same Idempotency-Key. Switching to a new key merely because the first response was lost can create a second valid code — and therefore a second license slot.

Identity boundary#

The create request contains no customer identity and no vendor reference. You never send a customer ID, name, email address, or account identifier to PropFirmConnector. Instead, you privately associate the returned discount_code_id and raw code with your customer on your side — discount_code_id is the durable cross-system bridge.

The raw code is a bearer capability. Show it only inside the intended customer's authenticated session, and keep it out of analytics, URLs, support transcripts, and shared logs. The first eligible PropFirmConnector account to complete durable redemption consumes it.

At durable redemption, PropFirmConnector records and returns exactly two identity snapshots:

pfc_account_name
The PropFirmConnector profile name at redemption. It is not a verified legal name.
pfc_account_email
The normalized, verified PropFirmConnector email at redemption.

Both values are immutable snapshots — they do not change if the profile changes later. No internal account UID, payment identifier, or other account identifier is ever exposed. Reconcile the snapshots to your own customer through your private discount_code_id mapping, and do not treat either snapshot as standalone proof of identity.

Discount codes#

The customer-facing code contains eight deterministic pseudorandom characters displayed as XXXX-XXXX, drawn from the Crockford Base32 alphabet:

Alphabet and canonical form
0123456789ABCDEFGHJKMNPQRSTVWXYZ

^[0-9A-HJ-NP-TV-Z]{4}-[0-9A-HJ-NP-TV-Z]{4}$

The alphabet omits I, L, O, and U. Codes are case-insensitive at checkout; O is accepted as an alias for 0, and I or L as aliases for 1. The hyphen may be omitted on input. Codes are derived deterministically from the create idempotency key under a server-only secret, which lets an idempotent replay return the same raw code without the raw value ever being stored — only a one-way hash and a masked form are kept.

Lifecycle#

A code expires at its offer-configured deadline; the launch offers use exactly 24 hours after created_at. Starting an eligible checkout before expires_at may create an internal reservation bound to the authenticated PropFirmConnector account and checkout attempt. A reservation obtained before expiry remains protected while PropFirmConnector reconciles the related payment operation, even when final confirmation arrives after expiry — the deadline prevents any new reservation but does not invalidate the in-flight one.

While no payment resource is attached, the reservation follows the customer's account rather than one abandoned browser attempt, so a reload or restarted checkout cannot permanently strand the code. Scheduled maintenance finalizes a reservation that still has no payment resource one hour after expires_at: the record becomes availability: expired and the code is never made reusable. A reservation with an attached payment resource is never age-released, and no name or email is exposed until consumption.

An unredeemed code has external status: unused. Availability is expressed separately:

availabilityMeaning
availableThe code can start checkout. redeemable is true only in this state.
reservedOne authenticated checkout holds it while PropFirmConnector confirms or reconciles the payment operation.
expiredIts offer-configured first-use window ended.
revokedPropFirmConnector support disabled it before redemption.
consumedIt has been assigned to a license slot.

Once a trial is activated or an immediate purchase succeeds, the code is permanently consumed. Cancellation, payment failure, expiration of access, refund, reversal, or chargeback never makes it reusable.

The discount_code object#

Create, status, list, and webhook payloads all use the same canonical discount_code object. Fields that are not yet applicable are null — they are not omitted.

discount_code
{
  "discount_code_id": "dcode_01K2F6P8JKR6J0CG4M2Y7W0A3P",
  "code_masked": "****-7K9P",
  "status": "monthly_active",
  "status_version": 3,
  "status_changed_at": "2026-08-19T16:04:12.000Z",
  "availability": "consumed",
  "redeemable": false,
  "created_at": "2026-08-12T15:58:10.000Z",
  "expires_at": "2026-08-13T15:58:10.000Z",
  "redemption": {
    "redeemed_at": "2026-08-12T16:03:44.000Z",
    "pfc_account_name": "Alex Trader",
    "pfc_account_email": "trader+pfc@example.com"
  },
  "entitlement": {
    "plan": "monthly",
    "state": "monthly_active"
  },
  "pii_purged": false
}

Timestamps are RFC 3339 UTC strings with a Z suffix. status_version starts at 1 and increases monotonically whenever the external status changes for this discount_code_id. Before a status or list object is returned, the stored state is transactionally synchronized with the code's exact linked entitlement, so each response describes one coherent revision. For a consumed code, entitlement.plan is monthly or lifetime, and entitlement.state derives from that code's exact license slot — never an account-wide aggregate.

Status values#

statusMeaning
unusedNo trial or entitlement has consumed the code. Check availability and redeemable to distinguish available, reserved, expired, or revoked.
trial_activeThe code is consumed, the temporary seven-day entitlement is active, and no plan payment has completed. This is not a paid sale.
trial_ended_unpaidThe trial consumed the code but ended without the first plan payment. The entitlement is inactive.
monthly_activeThe code's Monthly entitlement currently qualifies for access, including cancel-at-period-end while already-paid access remains.
monthly_inactiveThe Monthly entitlement does not currently qualify for access. It may return to monthly_active if payment recovery succeeds within the same agreement.
lifetimeThe Lifetime payment completed and the permanent entitlement currently qualifies for access.
payment_reversedA payment supporting the entitlement was fully refunded, reversed, or charged back and the entitlement was deactivated. The code remains consumed.

State transitions#

Normal transitions
unused -> trial_active -> monthly_active
unused -> trial_active -> lifetime
unused -> trial_active -> trial_ended_unpaid
unused -> monthly_active
unused -> lifetime
monthly_active -> monthly_inactive
monthly_inactive -> monthly_active
monthly_active -> payment_reversed
lifetime -> payment_reversed

The direct unused → monthly_active|lifetime transitions are immediate checkouts for accounts with prior PropFirmConnector history. payment_reversed is terminal for that license slot in v1 — resolving a dispute does not silently restore it, and renewed access requires a new qualifying purchase with a new unused code. A trial remains trial_active until its first successful plan payment or until access ends; selecting Lifetime during a trial keeps trial_active until the exact Lifetime charge succeeds. Only a full refund, reversal, or chargeback produces payment_reversed — a partial refund does not change the status. Payment amounts and provider-specific payment details are not returned by this API because prices are fixed in the server-owned offer registry. The API does not calculate or report commissions, fees, payouts, or revenue shares.

Create a discount code#

POST /vendor/v1/discount-codes

Requires the authentication headers and Idempotency-Key. The request body must be exactly an empty JSON object — every request field is rejected, including any customer ID, name, email, reference, phone number, address, or government identifier:

Request body
{}
201 Created
HTTP/1.1 201 Created
Location: /vendor/v1/discount-codes/dcode_01K2F6P8JKR6J0CG4M2Y7W0A3P

{
  "replayed": false,
  "discount_code": {
    "discount_code_id": "dcode_01K2F6P8JKR6J0CG4M2Y7W0A3P",
    "code": "Q7KM-7K9P",
    "code_masked": "****-7K9P",
    "status": "unused",
    "status_version": 1,
    "status_changed_at": "2026-08-12T15:58:10.000Z",
    "availability": "available",
    "redeemable": true,
    "created_at": "2026-08-12T15:58:10.000Z",
    "expires_at": "2026-08-13T15:58:10.000Z",
    "redemption": null,
    "entitlement": null,
    "pii_purged": false
  }
}
The raw code appears only in this response. Every later response contains code_masked only. Store the raw value securely if you need to show it again, and never place it in analytics, access logs, email subject lines, or URL query strings.

You may create more than one code for one customer — PropFirmConnector receives no issuance identity and performs no customer-level deduplication. Each code is a distinct potential license slot. Accidental duplicates are prevented by correct use of Idempotency-Key and your private discount_code_id mapping.

Get code & entitlement status#

GET /vendor/v1/discount-codes/{discount_code_id}

discount_code_id is the opaque ID returned at creation — never put the raw customer-facing code in this URL; the external status endpoint does not accept it. A successful lookup returns 200 with Cache-Control: no-store and the current canonical discount_code object.

The lookup resolves through the credential-derived vendor/environment index: a valid ID owned by another tenant returns 404 exactly like an unknown ID. Use webhooks as your primary update mechanism and this endpoint for confirmation or recovery. Conditional GET and ETag behavior are not part of v1.

List redeemed codes#

GET /vendor/v1/redemptions

Returns only codes that have been consumed — never status: unused, including expired unused codes. Results are ordered by redemption.redeemed_at descending.

ParameterRequiredRules
limitNoInteger 1–100. Default 50.
cursorNoOpaque cursor returned by the previous response.
200 OK
{
  "data": [
    {
      "discount_code_id": "dcode_01K2F6P8JKR6J0CG4M2Y7W0A3P",
      "code_masked": "****-7K9P",
      "status": "monthly_active",
      "status_version": 3,
      "status_changed_at": "2026-08-19T16:04:12.000Z",
      "availability": "consumed",
      "redeemable": false,
      "created_at": "2026-08-12T15:58:10.000Z",
      "expires_at": "2026-08-13T15:58:10.000Z",
      "redemption": {
        "redeemed_at": "2026-08-12T16:03:44.000Z",
        "pfc_account_name": "Alex Trader",
        "pfc_account_email": "trader+pfc@example.com"
      },
      "entitlement": {
        "plan": "monthly",
        "state": "monthly_active"
      },
      "pii_purged": false
    }
  ],
  "page": {
    "limit": 50,
    "has_more": true,
    "next_cursor": "eyJ2IjoxLCJhZnRlciI6Ii4uLiJ9..."
  }
}

next_cursor is null when has_more is false. An empty result is data: [], not 404. Cursors are opaque and tamper-evident, signed over the authenticated vendor and environment — a cursor cannot be replayed under another vendor or environment; an invalid or cross-tenant cursor returns 400 VENDOR_PROMOTION_INVALID_REQUEST.

Webhooks#

Webhooks are the primary mechanism for learning that one of your codes was consumed or that its entitlement status changed. Delivery is signed and at-least-once: duplicates and out-of-order arrival are possible, so deduplicate by event ID and order by status_version. Webhooks are optional — status and redemption-list polling remain available when delivery is disabled or not configured.

Endpoint requirements#

Your endpoint must use a publicly trusted HTTPS certificate on port 443, accept POST, and must not redirect. URLs containing credentials, query strings, fragments, localhost, or literal IP addresses are rejected. PropFirmConnector resolves your hostname both at configuration time and immediately before every delivery, rejects any non-public address, and never follows redirects.

Configuration is a two-step operation in the vendor portal: staging the URL creates a separate webhook signing key displayed exactly once; verification then sends a signed vendor.webhook.challenge payload, and your endpoint must verify the signature and return 2xx JSON echoing the exact received nonce as {"challenge":"<received-value>"}. Only a successful echo promotes the pending URL and key, and delivery stays disabled until you enable it. Changing a URL or rotating a key never interrupts a verified active configuration while the replacement is pending.

Event types#

EventEmitted when
vendor.discount_code.redeemedThe first durable transition from unused to a consumed status.
vendor.entitlement.status_changedAny later status change, including the first paid transition, Monthly activity changes, a trial ending unpaid, or Lifetime activation.
vendor.payment.reversedA qualifying payment was refunded, reversed, or charged back and status became payment_reversed.

Ignore unknown event types after recording their event ID — additive event types do not require a new API version.

Payload#

Example delivery
{
  "id": "evt_01K2Z2W6MV4BTJ5R3Z1P7Q8N9C",
  "type": "vendor.entitlement.status_changed",
  "api_version": "v1",
  "created_at": "2026-08-19T16:04:12Z",
  "environment": "live",
  "vendor_id": "your-vendor-id",
  "data": {
    "previous_status": "trial_active",
    "discount_code": { /* canonical discount_code object */ }
  }
}

The raw customer-facing code, payment identifiers, card details, and billing addresses are never included. vendor_id always equals the vendor bound to your webhook configuration — neither a payload nor an administrator request can redirect an event across tenants.

Signature verification#

Every delivery includes these headers:

Delivery headers
X-PFC-Webhook-Id: evt_01K2Z2W6MV4BTJ5R3Z1P7Q8N9C
X-PFC-Webhook-Timestamp: 1787155452
X-PFC-Webhook-Key-Id: whk_xxxxxxxxxxxxxxxx
Content-Digest: sha-256=:<standard-base64-sha256>:
X-PFC-Webhook-Signature: v1=<64-lowercase-hex-characters>
Content-Type: application/json

Compute Content-Digest as SHA-256 over the exact raw body bytes, encoded as standard padded base64 and wrapped as sha-256=:{digest}:. Build the canonical string by joining these four values with a single line-feed byte (0x0A) and no trailing line feed:

Canonical string
PFC-WEBHOOK-HMAC-SHA256-V1
{WEBHOOK_ID}
{WEBHOOK_TIMESTAMP}
{CONTENT_DIGEST_HEADER_VALUE}

Select the secret named by X-PFC-Webhook-Key-Id, compute HMAC-SHA256 over the canonical string, encode as 64 lowercase hex characters, prefix with v1=, and compare the complete header value in constant time. The key ID is a selector only — it is not an additional canonical-string line. After a rotation is promoted, PropFirmConnector signs only with the newly verified key.

Verification test vector#

This fixed vector is pinned byte-for-byte by PropFirmConnector's own executable tests. The test secret is the ASCII byte string api-signing-secret-for-unit-tests-000001 and the exact body is {"id":"event-one","status":"monthly_active"}:

Expected values
Content-Digest: sha-256=:omCmGngHElJyVgKGRI5gHCUSYUVwxWt6upHJACSG+ao=:
X-PFC-Webhook-Id: event-one
X-PFC-Webhook-Timestamp: 1700000000
X-PFC-Webhook-Key-Id: whk_test_vector
X-PFC-Webhook-Signature: v1=aaf3f4e5151d3933484955db5a3ad433c367a2c9f65d63c11fa8aee536d4b738
Its canonical string
PFC-WEBHOOK-HMAC-SHA256-V1
event-one
1700000000
sha-256=:omCmGngHElJyVgKGRI5gHCUSYUVwxWt6upHJACSG+ao=:

Receiver checklist#

  1. The key ID names your current webhook secret and the signature is valid under that exact key.
  2. The timestamp is no more than 300 seconds from your current time.
  3. The header event ID exactly equals the JSON id.
  4. The event ID has not already been applied.

Persist the event ID before acknowledging. Within a discount_code_id, apply only payloads whose status_version is greater than the last applied version, and still return success for duplicates and older versions.

Acknowledgment and retries#

Return any 2xx within four seconds; normal event response bodies are ignored (challenge deliveries are the exception and must echo the challenge). Network failures, timeouts, and every non-2xx response are retried: the dispatcher scans for due deliveries approximately every five minutes, the retry delay doubles from 30 seconds up to a 24-hour cap, and delivery stops after 12 total attempts. Retry-After is not honored in v1.

The vendor portal provides a synthetic test delivery, recent delivery summaries, enable/disable controls, staged signing-secret rotation, and replay for delivered or retry-exhausted events. Replays retain the original event ID and remain subject to your normal deduplication. After an extended outage, reconcile through the status and redemption-list endpoints.

At-least-once means exactly that. Never create credits, notifications, or downstream entitlements solely because an HTTP request arrived. Deduplicate by event ID and order by status_version.

Errors#

Every non-2xx API response uses this envelope when a JSON response can be produced:

Error envelope
{
  "error": "The request body must be an empty JSON object.",
  "code": "VENDOR_PROMOTION_INVALID_REQUEST"
}
HTTPcodeRetry?
400VENDOR_PROMOTION_INVALID_REQUESTNo — correct the request.
401VENDOR_AUTHENTICATION_FAILEDNo automatic retry until the bearer credential, environment, state, and scope are checked.
404VENDOR_PROMOTION_NOT_FOUNDNo. A resource outside your vendor's scope is also not found.
409VENDOR_PROMOTION_CONFLICTNo for an idempotency-key/body conflict; reconcile before any new create key.
413VENDOR_REQUEST_TOO_LARGENo.
422VENDOR_PROMOTION_UNAVAILABLENo without a new eligible code or resolved checkout state.
429VENDOR_RATE_LIMITEDYes, with client backoff.
503VENDOR_PROMOTION_SERVICE_UNAVAILABLEYes, idempotently.

Messages are safe for an operator but not intended for direct customer display. Authentication errors deliberately reveal no failing component, and validation errors never echo secrets or the raw request body.

Rate limits & retries#

Initial per-vendor limits, shared by operation category within your vendor and environment (rotating keys cannot create a fresh bucket):

OperationLimit
Create10 requests per minute
Status120 requests per minute
Redeemed list60 requests per minute

A rolling per-vendor issuance quota also applies; details are provided during onboarding. Limits may be raised by agreement or reduced temporarily for abuse protection without changing the API version. Rate-limit response headers are not part of v1.

Recommended client retry policy:

  1. Use connection and response timeouts.
  2. Retry up to five times with exponential backoff and full jitter.
  3. For GET, retry 408, 429, 500, 502, 503, and 504. For create, retry the same statuses only with the original body and Idempotency-Key under the same bearer credential.
  4. Apply local backoff on 429 and 503; v1 does not promise a Retry-After header.
  5. Stop on other 4xx responses.
  6. If all create retries fail, search your operational logs by idempotency key or contact PropFirmConnector before issuing a different key.

Data retention & security#

RecordRetention
Pending API credential24 hours unless activated or revoked sooner
Pending webhook endpoint / signing secretUsable for 24 hours unless verified or replaced sooner
Idempotency records and stored create responses12 months
Expired or revoked unused code record12 months after creation, so the create-idempotency guarantee stays verifiable
Redeemed record, including the name/email snapshotsWhile the entitlement is active, then 12 months after it becomes inactive
Webhook event body and delivery-attempt detail90 days from event occurrence
API security/access audit events90 days
Webhook administration audit events90 days

For retention, trial_active, monthly_active, and lifetime are active; trial_ended_unpaid, monthly_inactive, and payment_reversed are inactive and start the 12-month clock. If a Monthly record returns to active before deletion, the clock stops and restarts the next time it becomes inactive. At the end of the window the name and email snapshots are deleted or irreversibly anonymized and the canonical object reports pii_purged: true; the non-PII code and status record remains available for reconciliation. A targeted legal or dispute hold may suspend deletion only for the affected record.

Bearer credentials and customer-facing codes are stored only as one-way hashes after creation. Bearer credentials, raw codes, full request bodies, and payment details are excluded from application logs, and profile names and emails are never written to access logs. Data is encrypted in transit and at rest, and credential-derived tenant access lets a vendor read only its own records in the authenticated environment.

You are responsible for protecting the create response and all API and webhook data, limiting employee access, and encrypting stored credentials and customer mappings. Delete the name/email snapshots no later than 12 months after the entitlement becomes inactive unless a legal obligation requires a narrower targeted hold, and report any suspected credential, code, or PII leak promptly so the affected key or webhook secret can be revoked.

Go-live#

Onboarding is direct to production: once your offer is configured and your production credentials are issued, the codes you create are live. Production enablement requires secret-storage confirmation and at least one enrolled administrator in the vendor portal. Confirmed webhook verification (the challenge echo) is required only before you enable webhook delivery — polling may launch without it. PropFirmConnector's own checkout tells the redeeming customer what is shared with you before the code is consumed — nothing is required on your side.

Before relying on your integration, verify on your side that:

  1. Your webhook receiver validates the published byte-exact signature test vector locally — no live delivery is needed to prove your verification code.
  2. Your create path retries a lost response with the same Idempotency-Key and never re-keys.
  3. Your receiver deduplicates by event ID and applies updates in status_version order, returning success for duplicates.
  4. Your reconciliation path can rebuild state from the status and redemption-list endpoints after an outage.

Coordinate your first issued code with PropFirmConnector so both sides confirm redemption, webhook delivery, and status reads end to end.

Versioning#

v1 is a stable contract: additive response fields and new webhook event types may appear at any time and must be tolerated; unknown request fields are always rejected; breaking changes require a new major path such as /vendor/v2. Request IDs, redemption-list filters, and cursor expiration beyond the documented limit/cursor behavior are candidates for later versions and must not be relied on until documented here. Webhook signature changes require a new explicit protocol marker and an overlap plan.

Getting access#

The Vendor API is available to approved partners. Onboarding covers your offer configuration (plans, discount, code lifetime), production credentials, and go-live coordination.

Start on the Vendor API overview, or email support@propfirmconnector.com with your firm name and website to request access.

Authenticator verification

Enter your authentication code

Enter the current 6-digit code from your authenticator app.