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:
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:
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.
Required headers#
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:createforPOST /vendor/v1/discount-codesdiscount_codes:readforGET /vendor/v1/discount-codes/{discount_code_id}redemptions:listforGET /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:
- Create a replacement key and store its one-time bearer value immediately.
- Send one valid, in-scope request with it; the key becomes active.
- 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:
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 returns200withreplayed: true. -
Reusing the key with a different body returns
409with error codeVENDOR_PROMOTION_CONFLICTand 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.
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:
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:
| availability | Meaning |
|---|---|
available | The code can start checkout. redeemable is true only in this state. |
reserved | One authenticated checkout holds it while PropFirmConnector confirms or reconciles the payment operation. |
expired | Its offer-configured first-use window ended. |
revoked | PropFirmConnector support disabled it before redemption. |
consumed | It 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_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#
| status | Meaning |
|---|---|
unused | No trial or entitlement has consumed the code. Check availability and redeemable to distinguish available, reserved, expired, or revoked. |
trial_active | The code is consumed, the temporary seven-day entitlement is active, and no plan payment has completed. This is not a paid sale. |
trial_ended_unpaid | The trial consumed the code but ended without the first plan payment. The entitlement is inactive. |
monthly_active | The code's Monthly entitlement currently qualifies for access, including cancel-at-period-end while already-paid access remains. |
monthly_inactive | The Monthly entitlement does not currently qualify for access. It may return to monthly_active if payment recovery succeeds within the same agreement. |
lifetime | The Lifetime payment completed and the permanent entitlement currently qualifies for access. |
payment_reversed | A payment supporting the entitlement was fully refunded, reversed, or charged back and the entitlement was deactivated. The code remains consumed. |
State 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#
/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:
{} 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
}
} 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#
/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#
/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.
| Parameter | Required | Rules |
|---|---|---|
limit | No | Integer 1–100. Default 50. |
cursor | No | Opaque cursor returned by the previous response. |
{
"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#
| Event | Emitted when |
|---|---|
vendor.discount_code.redeemed | The first durable transition from unused to a consumed status. |
vendor.entitlement.status_changed | Any later status change, including the first paid transition, Monthly activity changes, a trial ending unpaid, or Lifetime activation. |
vendor.payment.reversed | A 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#
{
"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:
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:
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"}:
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 PFC-WEBHOOK-HMAC-SHA256-V1
event-one
1700000000
sha-256=:omCmGngHElJyVgKGRI5gHCUSYUVwxWt6upHJACSG+ao=: Receiver checklist#
- The key ID names your current webhook secret and the signature is valid under that exact key.
- The timestamp is no more than 300 seconds from your current time.
- The header event ID exactly equals the JSON
id. - 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.
status_version.
Errors#
Every non-2xx API response uses this envelope when a JSON response can be produced:
{
"error": "The request body must be an empty JSON object.",
"code": "VENDOR_PROMOTION_INVALID_REQUEST"
} | HTTP | code | Retry? |
|---|---|---|
| 400 | VENDOR_PROMOTION_INVALID_REQUEST | No — correct the request. |
| 401 | VENDOR_AUTHENTICATION_FAILED | No automatic retry until the bearer credential, environment, state, and scope are checked. |
| 404 | VENDOR_PROMOTION_NOT_FOUND | No. A resource outside your vendor's scope is also not found. |
| 409 | VENDOR_PROMOTION_CONFLICT | No for an idempotency-key/body conflict; reconcile before any new create key. |
| 413 | VENDOR_REQUEST_TOO_LARGE | No. |
| 422 | VENDOR_PROMOTION_UNAVAILABLE | No without a new eligible code or resolved checkout state. |
| 429 | VENDOR_RATE_LIMITED | Yes, with client backoff. |
| 503 | VENDOR_PROMOTION_SERVICE_UNAVAILABLE | Yes, 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):
| Operation | Limit |
|---|---|
| Create | 10 requests per minute |
| Status | 120 requests per minute |
| Redeemed list | 60 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:
- Use connection and response timeouts.
- Retry up to five times with exponential backoff and full jitter.
-
For
GET, retry408,429,500,502,503, and504. For create, retry the same statuses only with the original body andIdempotency-Keyunder the same bearer credential. - Apply local backoff on
429and503; v1 does not promise aRetry-Afterheader. - Stop on other
4xxresponses. - If all create retries fail, search your operational logs by idempotency key or contact PropFirmConnector before issuing a different key.
Data retention & security#
| Record | Retention |
|---|---|
| Pending API credential | 24 hours unless activated or revoked sooner |
| Pending webhook endpoint / signing secret | Usable for 24 hours unless verified or replaced sooner |
| Idempotency records and stored create responses | 12 months |
| Expired or revoked unused code record | 12 months after creation, so the create-idempotency guarantee stays verifiable |
| Redeemed record, including the name/email snapshots | While the entitlement is active, then 12 months after it becomes inactive |
| Webhook event body and delivery-attempt detail | 90 days from event occurrence |
| API security/access audit events | 90 days |
| Webhook administration audit events | 90 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:
- Your webhook receiver validates the published byte-exact signature test vector locally — no live delivery is needed to prove your verification code.
-
Your create path retries a lost response with the same
Idempotency-Keyand never re-keys. -
Your receiver deduplicates by event ID and applies updates in
status_versionorder, returning success for duplicates. - 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.