{
  "schema_version": 1,
  "type_base": "https://docs.perionpayments.com/problems/",
  "vocabulary": {
    "retry": {
      "same_request": "Repeat the identical request now, with the same Idempotency-Key and body.",
      "after_new_token": "Get a new access token from POST /oauth/token, then repeat the identical request with the same Idempotency-Key and body.",
      "after_delay": "Repeat the identical request after the Retry-After delay, or after a short wait when none is given, with the same Idempotency-Key and body.",
      "after_merchant_action": "A person must change something first: someone at the merchant, or PerionPayments when it is the one to act. After that, the same request can succeed.",
      "never": "Don't repeat this request. Fix what the response describes, or stop and report it."
    },
    "who_acts": {
      "caller": "You: the software that sent the request.",
      "merchant": "A person at the merchant, such as an Owner in the dashboard.",
      "payer": "The customer who is paying.",
      "perionpayments": "PerionPayments."
    },
    "do_not": {
      "create_another_payment": "Don't start another payment for the same order.",
      "ask_customer_to_pay_again": "Don't ask the customer to pay again.",
      "send_another_prompt": "Don't send the customer another payment prompt.",
      "fulfill_order": "Don't fulfill the order on the strength of this response.",
      "change_idempotency_key": "Don't retry with a new Idempotency-Key; a new key is a new request.",
      "work_around_limit": "Don't split, resize or reroute the request to get around this limit.",
      "bypass_merchant_decision": "Don't use another key or route to get around the merchant's decision.",
      "repeat_unchanged": "Don't send the same request again unchanged; it will be refused the same way.",
      "ask_for_pin_or_code": "Never ask the customer for a PIN, password or one-time code."
    }
  },
  "problems": [
    {
      "code": "agreement_acceptance_required",
      "type": "https://docs.perionpayments.com/problems/agreement-acceptance-required",
      "title": "Billing agreement acceptance required",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Ask the customer to review and accept the recurring terms on the checkout page, then confirm once they have.",
      "do_not": [],
      "meaning": "This checkout starts a recurring plan and the customer hasn't accepted its current terms. Nothing was sent to the customer and no money moved.",
      "carries_existing": false
    },
    {
      "code": "amount_above_method_maximum",
      "type": "https://docs.perionpayments.com/problems/amount-above-method-maximum",
      "title": "Payment exceeds the per-payment limit",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Choose a payment method whose per-payment limit covers the whole amount (GET /v1/payment_methods lists each method's range); never split one order into smaller payments.",
      "do_not": [],
      "meaning": "The amount is above the most this payment method accepts in one payment on the merchant's account. No payment or payment link was created.",
      "carries_existing": false
    },
    {
      "code": "amount_below_method_minimum",
      "type": "https://docs.perionpayments.com/problems/amount-below-method-minimum",
      "title": "Payment is below the per-payment minimum",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Choose a payment method whose minimum allows this amount (GET /v1/payment_methods lists each method's range).",
      "do_not": [],
      "meaning": "The amount is below the least this payment method accepts in one payment on the merchant's account. No payment or payment link was created.",
      "carries_existing": false
    },
    {
      "code": "api_client_expired",
      "type": "https://docs.perionpayments.com/problems/api-client-expired",
      "title": "API client expired",
      "status": [
        401,
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "merchant",
      "next_step": "Stop and ask the merchant to issue a new API client; this one can no longer get tokens.",
      "do_not": [],
      "meaning": "The API client behind this token has passed its expiry date. Nothing was done, and no new token can be issued for it.",
      "carries_existing": false
    },
    {
      "code": "api_client_paused",
      "type": "https://docs.perionpayments.com/problems/api-client-paused",
      "title": "API client paused",
      "status": [
        403
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "merchant",
      "next_step": "Stop and tell the merchant; requests from this API client succeed again only after they resume it.",
      "do_not": [
        "bypass_merchant_decision"
      ],
      "meaning": "The merchant paused this API client. Its requests are refused until they resume it; payments it already started carry on and still get a final result.",
      "carries_existing": false
    },
    {
      "code": "api_client_policy_amount_exceeded",
      "type": "https://docs.perionpayments.com/problems/api-client-policy-amount-exceeded",
      "title": "Amount exceeds this API client's limit",
      "status": [
        422
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "merchant",
      "next_step": "Stop and tell the merchant the amount is above this API client's per-payment limit; only a key with a higher limit can take it.",
      "do_not": [
        "work_around_limit"
      ],
      "meaning": "The amount is above the per-payment limit the merchant set for this API client in this currency. Nothing was created and no money moved.",
      "carries_existing": false
    },
    {
      "code": "api_client_policy_operation_not_allowed",
      "type": "https://docs.perionpayments.com/problems/api-client-policy-operation-not-allowed",
      "title": "Operation not allowed for this API client",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "merchant",
      "next_step": "Stop and tell the merchant this API client isn't allowed to do this.",
      "do_not": [
        "bypass_merchant_decision"
      ],
      "meaning": "The merchant issued this API client with terms that don't include this operation. Nothing was created and no money moved.",
      "carries_existing": false
    },
    {
      "code": "api_client_policy_prompt_reach_exceeded",
      "type": "https://docs.perionpayments.com/problems/api-client-policy-prompt-reach-exceeded",
      "title": "Too many customers prompted",
      "status": [
        429
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "caller",
      "next_step": "Wait for Retry-After before prompting another customer, and send a payment link to customers who haven't asked to pay yet.",
      "do_not": [
        "send_another_prompt"
      ],
      "meaning": "This API client has sent payment prompts to as many different customers as its terms allow this hour. Nothing was sent to the customer's phone.",
      "carries_existing": false
    },
    {
      "code": "api_client_policy_refund_limit_exceeded",
      "type": "https://docs.perionpayments.com/problems/api-client-policy-refund-limit-exceeded",
      "title": "Refund exceeds this API client's limit",
      "status": [
        422
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "merchant",
      "next_step": "Stop and tell the merchant the refund is above this API client's refund limit; a person can refund it from the dashboard.",
      "do_not": [
        "work_around_limit"
      ],
      "meaning": "The refund is above a limit the merchant set for this API client: per refund, per day, or which payments it may refund. Nothing was refunded.",
      "carries_existing": false
    },
    {
      "code": "attempt_in_flight",
      "type": "https://docs.perionpayments.com/problems/attempt-in-flight",
      "title": "Payment already sent to the customer",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Read the payment until it has a final result; it has already reached the customer or the payment provider, so it can't be canceled now.",
      "do_not": [
        "create_another_payment",
        "ask_customer_to_pay_again"
      ],
      "meaning": "The payment was already sent to the customer's phone or the payment provider, so a cancel can't decide its result. It will still end as paid or not paid.",
      "carries_existing": false
    },
    {
      "code": "billing_control_review_conflict",
      "type": "https://docs.perionpayments.com/problems/billing-control-review-conflict",
      "title": "Subscription changed after review",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the subscription and its agreement again, review the action against them, then send it as a new request.",
      "do_not": [],
      "meaning": "The subscription or its agreement changed after you reviewed it. Nothing was changed.",
      "carries_existing": false
    },
    {
      "code": "billing_reconsent_review_conflict",
      "type": "https://docs.perionpayments.com/problems/billing-reconsent-review-conflict",
      "title": "Source changed after review",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the current subscription and agreement, review the replacement terms again, then send a new request.",
      "do_not": [],
      "meaning": "The subscription or agreement being replaced changed after you reviewed it. No replacement was started.",
      "carries_existing": false
    },
    {
      "code": "billing_settings_review_conflict",
      "type": "https://docs.perionpayments.com/problems/billing-settings-review-conflict",
      "title": "Billing settings changed after review",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the current collection policy or test clock, review it, then send a new request.",
      "do_not": [],
      "meaning": "Billing settings changed after you reviewed them. Nothing was changed.",
      "carries_existing": false
    },
    {
      "code": "billing_state_conflict",
      "type": "https://docs.perionpayments.com/problems/billing-state-conflict",
      "title": "Billing state changed",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Reload the plan page and use only the controls it shows now.",
      "do_not": [],
      "meaning": "The plan changed since the page was loaded, so this action no longer applies. Nothing was changed.",
      "carries_existing": false
    },
    {
      "code": "bulk_replay_scope_changed",
      "type": "https://docs.perionpayments.com/problems/bulk-replay-scope-changed",
      "title": "Replay scope changed",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the endpoint's delivery log again, review the new count, then send a replay for it.",
      "do_not": [],
      "meaning": "The number of undelivered events changed after you reviewed the replay. Nothing was replayed.",
      "carries_existing": false
    },
    {
      "code": "capability_not_granted",
      "type": "https://docs.perionpayments.com/problems/capability-not-granted",
      "title": "Capability not granted",
      "status": [
        403
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "perionpayments",
      "next_step": "Tell the merchant this isn't enabled for their account or this API client; PerionPayments enables it.",
      "do_not": [],
      "meaning": "This merchant or API client isn't permitted to do this. Nothing was created and no money moved.",
      "carries_existing": false
    },
    {
      "code": "capability_suspended",
      "type": "https://docs.perionpayments.com/problems/capability-suspended",
      "title": "Paused for review",
      "status": [
        403
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "perionpayments",
      "next_step": "Tell the merchant PerionPayments has paused this for their business while it reviews it; support can explain.",
      "do_not": [],
      "meaning": "This is paused for the business during a PerionPayments review. Nothing was created and no money moved.",
      "carries_existing": false
    },
    {
      "code": "card_attempt_limit_reached",
      "type": "https://docs.perionpayments.com/problems/card-attempt-limit-reached",
      "title": "Card attempt limit reached",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Offer the customer another payment method on this checkout.",
      "do_not": [],
      "meaning": "Three card attempts failed on this checkout, so it no longer accepts cards. The customer can still pay another way.",
      "carries_existing": false
    },
    {
      "code": "checkout_enrollment_not_ready",
      "type": "https://docs.perionpayments.com/problems/checkout-enrollment-not-ready",
      "title": "Enrollment is not ready",
      "status": [
        503
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "merchant",
      "next_step": "Ask the merchant to check this recurring enrollment; don't create a replacement checkout.",
      "do_not": [
        "ask_customer_to_pay_again",
        "create_another_payment"
      ],
      "meaning": "The recurring terms for this checkout aren't available yet, so it can't take the first payment. Nothing was sent to the customer and no money moved.",
      "carries_existing": false
    },
    {
      "code": "checkout_session_expired",
      "type": "https://docs.perionpayments.com/problems/checkout-session-expired",
      "title": "Checkout session expired",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the checkout's payment; start a new checkout only if that payment ended without being paid.",
      "do_not": [],
      "meaning": "This checkout has expired and can't start a payment. A payment it already started keeps its own result.",
      "carries_existing": false
    },
    {
      "code": "contact_submission_conflict",
      "type": "https://docs.perionpayments.com/problems/contact-submission-conflict",
      "title": "Submission changed",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Submit the form as a new request with a new submission identity.",
      "do_not": [],
      "meaning": "The same submission identity was used with different details. Nothing new was recorded.",
      "carries_existing": false
    },
    {
      "code": "continuation_unavailable",
      "type": "https://docs.perionpayments.com/problems/continuation-unavailable",
      "title": "Continuation unavailable",
      "status": [
        404
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Check the resume code, or ask the merchant for a fresh payment link.",
      "do_not": [],
      "meaning": "This resume code doesn't open a payment. It says nothing about whether an earlier payment succeeded.",
      "carries_existing": false
    },
    {
      "code": "customer_version_conflict",
      "type": "https://docs.perionpayments.com/problems/customer-version-conflict",
      "title": "Customer changed",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the customer again and review the latest contact details before saving.",
      "do_not": [],
      "meaning": "The customer changed since you read it. Nothing was saved.",
      "carries_existing": false
    },
    {
      "code": "daily_value_limit_reached",
      "type": "https://docs.perionpayments.com/problems/daily-value-limit-reached",
      "title": "Daily limit reached",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Offer another payment method, or take this payment on the next business day.",
      "do_not": [],
      "meaning": "This payment would take the payment method past its daily value limit on the merchant's account. No payment was sent.",
      "carries_existing": false
    },
    {
      "code": "download_token_expired",
      "type": "https://docs.perionpayments.com/problems/download-token-expired",
      "title": "Download token expired",
      "status": [
        410
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Create a new download session for this export and use its token.",
      "do_not": [],
      "meaning": "This download token has expired. The export itself is unchanged.",
      "carries_existing": false
    },
    {
      "code": "download_token_used",
      "type": "https://docs.perionpayments.com/problems/download-token-used",
      "title": "Download token already used",
      "status": [
        410
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Create a new download session for this export and use its token.",
      "do_not": [],
      "meaning": "This download token was already used once. The export itself is unchanged.",
      "carries_existing": false
    },
    {
      "code": "export_expired",
      "type": "https://docs.perionpayments.com/problems/export-expired",
      "title": "Export expired",
      "status": [
        410
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Run the same export again to get a fresh file.",
      "do_not": [],
      "meaning": "The export file passed its retention time and was deleted.",
      "carries_existing": false
    },
    {
      "code": "export_not_ready",
      "type": "https://docs.perionpayments.com/problems/export-not-ready",
      "title": "Export is not ready",
      "status": [
        409
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "caller",
      "next_step": "Read the export job until it is ready, then create a download session.",
      "do_not": [],
      "meaning": "The export is still being prepared.",
      "carries_existing": false
    },
    {
      "code": "idempotency_key_missing",
      "type": "https://docs.perionpayments.com/problems/idempotency-key-missing",
      "title": "Idempotency key required",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Add an Idempotency-Key header of 16 to 255 characters, unique to this operation, then send the request.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "Requests that change something need an Idempotency-Key, so a retry can never act twice. Nothing was done.",
      "carries_existing": false
    },
    {
      "code": "idempotency_key_reused",
      "type": "https://docs.perionpayments.com/problems/idempotency-key-reused",
      "title": "Idempotency key reused",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Resend the original body unchanged with this key, or use a new key only if this is genuinely a different request.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "This Idempotency-Key was already used for a different request. Nothing new was done.",
      "carries_existing": false
    },
    {
      "code": "idempotency_request_in_progress",
      "type": "https://docs.perionpayments.com/problems/idempotency-request-in-progress",
      "title": "Request in progress",
      "status": [
        409
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "caller",
      "next_step": "Wait for Retry-After, then repeat the same request with the same Idempotency-Key and body.",
      "do_not": [
        "change_idempotency_key"
      ],
      "meaning": "The first request with this key is still running. Repeating it returns that request's result rather than acting again.",
      "carries_existing": false
    },
    {
      "code": "intent_not_cancellable",
      "type": "https://docs.perionpayments.com/problems/intent-not-cancellable",
      "title": "Payment intent cannot be canceled",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the payment; it has already finished or can no longer be canceled.",
      "do_not": [],
      "meaning": "This payment can't be canceled in its current state. Nothing changed.",
      "carries_existing": false
    },
    {
      "code": "intent_not_confirmable",
      "type": "https://docs.perionpayments.com/problems/intent-not-confirmable",
      "title": "Payment can't join this checkout",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Create the checkout for a payment that hasn't started yet, or keep using the checkout that already holds this payment.",
      "do_not": [],
      "meaning": "The payment has already moved on, expired or belongs to another checkout, so it can't be placed in this one. Nothing was created.",
      "carries_existing": false
    },
    {
      "code": "internal_error",
      "type": "https://docs.perionpayments.com/problems/internal-error",
      "title": "Internal error",
      "status": [
        500
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "perionpayments",
      "next_step": "Wait a moment, then repeat the request with the same Idempotency-Key; if it was meant to move money, read the payment or refund before doing anything else.",
      "do_not": [
        "change_idempotency_key",
        "create_another_payment"
      ],
      "meaning": "The request didn't finish normally. A request that moves money may or may not have taken effect, which is why repeating it with the same key is the safe way to find out.",
      "carries_existing": false
    },
    {
      "code": "internal_response_contract",
      "type": "https://docs.perionpayments.com/problems/internal-response-contract",
      "title": "Response could not be published",
      "status": [
        500
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "perionpayments",
      "next_step": "After a short wait, read the object again or repeat the request with the same Idempotency-Key.",
      "do_not": [
        "change_idempotency_key",
        "create_another_payment"
      ],
      "meaning": "The request may have completed, but its response couldn't be sent safely.",
      "carries_existing": false
    },
    {
      "code": "invalid_amount",
      "type": "https://docs.perionpayments.com/problems/invalid-amount",
      "title": "Invalid amount",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Send the amount once, as a positive whole number in minor units (cents).",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The amount is missing or isn't a positive whole number of minor units.",
      "carries_existing": false
    },
    {
      "code": "invalid_billing_action",
      "type": "https://docs.perionpayments.com/problems/invalid-billing-action",
      "title": "Invalid billing action",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Choose pause, resume or cancel.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "That billing action isn't recognized. Nothing changed.",
      "carries_existing": false
    },
    {
      "code": "invalid_checkout_secret",
      "type": "https://docs.perionpayments.com/problems/invalid-checkout-secret",
      "title": "Checkout authentication failed",
      "status": [
        401
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Open the checkout from its original link, or ask the merchant for a fresh one.",
      "do_not": [],
      "meaning": "The checkout link is incomplete, invalid or no longer available. Nothing was done.",
      "carries_existing": false
    },
    {
      "code": "invalid_contact_request",
      "type": "https://docs.perionpayments.com/problems/invalid-contact-request",
      "title": "Check your details",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Correct the fields named in detail, then submit again.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "Some details in the request aren't valid. Nothing was sent.",
      "carries_existing": false
    },
    {
      "code": "invalid_cursor",
      "type": "https://docs.perionpayments.com/problems/invalid-cursor",
      "title": "Invalid pagination cursor",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Start the list again from its first page and pass next_cursor exactly as it was returned.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The pagination cursor is malformed or belongs to another list or API version.",
      "carries_existing": false
    },
    {
      "code": "invalid_download_token",
      "type": "https://docs.perionpayments.com/problems/invalid-download-token",
      "title": "Invalid download token",
      "status": [
        400,
        401
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Use the one-use token returned when you created the download session, or create a new session.",
      "do_not": [],
      "meaning": "The download token is missing or invalid.",
      "carries_existing": false
    },
    {
      "code": "invalid_export",
      "type": "https://docs.perionpayments.com/problems/invalid-export",
      "title": "Invalid export request",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Correct the export request as detail describes: a supported report, a date range and the filters it accepts.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The export request isn't valid. No export was started.",
      "carries_existing": false
    },
    {
      "code": "invalid_filter",
      "type": "https://docs.perionpayments.com/problems/invalid-filter",
      "title": "Invalid list filter",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Correct the filter named in detail; unknown or repeated query parameters are refused.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "A list filter or query parameter isn't valid. Nothing was returned.",
      "carries_existing": false
    },
    {
      "code": "invalid_receipt_contact",
      "type": "https://docs.perionpayments.com/problems/invalid-receipt-contact",
      "title": "Check your receipt choice",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Enter a valid email address, or turn the SMS receipt on or off, then confirm.",
      "do_not": [],
      "meaning": "The receipt choice isn't valid. Nothing was sent to the customer.",
      "carries_existing": false
    },
    {
      "code": "invalid_request",
      "type": "https://docs.perionpayments.com/problems/invalid-request",
      "title": "Invalid request",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Fix the request as the detail and any field errors describe, then send it.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The request was refused as sent. Nothing was created and no money moved.",
      "carries_existing": false
    },
    {
      "code": "invalid_return_url",
      "type": "https://docs.perionpayments.com/problems/invalid-return-url",
      "title": "Invalid return URL",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Use an absolute HTTPS URL without user information or a fragment.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The return URL isn't acceptable. Nothing was created.",
      "carries_existing": false
    },
    {
      "code": "invalid_token",
      "type": "https://docs.perionpayments.com/problems/invalid-token",
      "title": "Authentication failed",
      "status": [
        401
      ],
      "retryable": true,
      "retry": "after_new_token",
      "who_acts": "caller",
      "next_step": "Get a new access token from POST /oauth/token, then repeat the request with the same Idempotency-Key.",
      "do_not": [
        "change_idempotency_key"
      ],
      "meaning": "The bearer token is missing, invalid or expired. Nothing was done.",
      "carries_existing": false
    },
    {
      "code": "lifecycle_conflict",
      "type": "https://docs.perionpayments.com/problems/lifecycle-conflict",
      "title": "Payment state changed",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the payment again and repeat the operation only if its current state still allows it.",
      "do_not": [
        "create_another_payment"
      ],
      "meaning": "The payment changed while this request ran. Nothing was changed by this request.",
      "carries_existing": false
    },
    {
      "code": "live_pilot_disabled",
      "type": "https://docs.perionpayments.com/problems/live-pilot-disabled",
      "title": "Live payments are not enabled",
      "status": [
        403
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "perionpayments",
      "next_step": "Tell the merchant live payments aren't enabled for their account; keep using Sandbox until PerionPayments enables Live.",
      "do_not": [],
      "meaning": "This merchant isn't admitted to live payments at the moment. No payment was sent.",
      "carries_existing": false
    },
    {
      "code": "live_pilot_expired",
      "type": "https://docs.perionpayments.com/problems/live-pilot-expired",
      "title": "The live pilot has ended",
      "status": [
        403
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "perionpayments",
      "next_step": "Tell the merchant live payments are paused until PerionPayments renews their live access.",
      "do_not": [],
      "meaning": "The merchant's live payment period has ended. No payment was sent.",
      "carries_existing": false
    },
    {
      "code": "merchant_reference_reused",
      "type": "https://docs.perionpayments.com/problems/merchant-reference-reused",
      "title": "Merchant reference reused",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Continue with the payment, payment link or refund in existing; use a new merchant_reference only for a genuinely different order.",
      "do_not": [
        "create_another_payment"
      ],
      "meaning": "Another payment, payment link or refund already uses this merchant reference. Nothing new was created; existing describes the one that has it.",
      "carries_existing": true
    },
    {
      "code": "mtls_required",
      "type": "https://docs.perionpayments.com/problems/mtls-required",
      "title": "Mutual TLS required",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Call this endpoint over the mutual-TLS connection issued to the merchant.",
      "do_not": [],
      "meaning": "This scope needs a verified mutual-TLS client connection. Nothing was returned.",
      "carries_existing": false
    },
    {
      "code": "order_already_paid",
      "type": "https://docs.perionpayments.com/problems/order-already-paid",
      "title": "This order is already paid",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Fulfill the order once, from the paid payment in existing.",
      "do_not": [
        "create_another_payment",
        "ask_customer_to_pay_again"
      ],
      "meaning": "A payment for this order_reference already succeeded. No new payment was created and nothing was sent to the customer.",
      "carries_existing": true
    },
    {
      "code": "order_payment_unresolved",
      "type": "https://docs.perionpayments.com/problems/order-payment-unresolved",
      "title": "This order already has a payment in progress",
      "status": [
        409
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "payer",
      "next_step": "Continue with the payment in existing: confirm it if it hasn't been sent yet, otherwise wait for its result. Please don't pay again.",
      "do_not": [
        "create_another_payment",
        "ask_customer_to_pay_again"
      ],
      "meaning": "This order already has a payment that hasn't finished: it is waiting for the customer, still confirming, or not yet sent. No new payment was created and nothing was sent to the customer.",
      "carries_existing": true
    },
    {
      "code": "payer_prompt_limit_reached",
      "type": "https://docs.perionpayments.com/problems/payer-prompt-limit-reached",
      "title": "Too many prompts for this payer",
      "status": [
        429
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "payer",
      "next_step": "Wait for Retry-After and for the customer to answer the prompts already on their phone, then repeat the same request.",
      "do_not": [
        "send_another_prompt",
        "create_another_payment"
      ],
      "meaning": "This phone number already has the most payment prompts allowed from this merchant, waiting or in the last hour. Nothing was sent to the phone.",
      "carries_existing": false
    },
    {
      "code": "payment_link_in_flight",
      "type": "https://docs.perionpayments.com/problems/payment-link-in-flight",
      "title": "Payment from this link is still confirming",
      "status": [
        409
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "payer",
      "next_step": "Wait for the payment already started from this link to finish, then read its result.",
      "do_not": [
        "create_another_payment",
        "ask_customer_to_pay_again"
      ],
      "meaning": "A payment from this one-time link is still waiting for the customer or still confirming. No new checkout was started.",
      "carries_existing": false
    },
    {
      "code": "payment_link_not_active",
      "type": "https://docs.perionpayments.com/problems/payment-link-not-active",
      "title": "Payment link is not active",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the link: if it is paid, the order is paid; if it expired or was turned off, create a new link.",
      "do_not": [],
      "meaning": "The link is paid, expired or turned off, so it can't start a new checkout or be turned off again.",
      "carries_existing": false
    },
    {
      "code": "payment_method_requires_hosted_checkout",
      "type": "https://docs.perionpayments.com/problems/payment-method-requires-hosted-checkout",
      "title": "Use hosted checkout for this payment",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Create a checkout session for this payment and send the customer its url; the customer enters any code on that page.",
      "do_not": [
        "ask_for_pin_or_code",
        "repeat_unchanged"
      ],
      "meaning": "This payment method asks the customer for a one-time code, which only the hosted checkout page may collect. Nothing was sent to the customer.",
      "carries_existing": false
    },
    {
      "code": "payment_method_unavailable",
      "type": "https://docs.perionpayments.com/problems/payment-method-unavailable",
      "title": "Payment method unavailable",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Choose a method listed by GET /v1/payment_methods for this amount and currency.",
      "do_not": [],
      "meaning": "That payment method isn't available for this amount, currency or account right now. No payment was sent and no payment link was created.",
      "carries_existing": false
    },
    {
      "code": "pilot_payer_not_allowed",
      "type": "https://docs.perionpayments.com/problems/pilot-payer-not-allowed",
      "title": "Payer is outside the pilot",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "perionpayments",
      "next_step": "Tell the merchant this customer can't pay live during the pilot; they can pay another way.",
      "do_not": [],
      "meaning": "During the live pilot only listed payers can pay, and this number isn't one of them. No payment was sent.",
      "carries_existing": false
    },
    {
      "code": "pilot_transaction_cap",
      "type": "https://docs.perionpayments.com/problems/pilot-transaction-cap",
      "title": "Payment exceeds the pilot limit",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Use an amount within the live pilot's per-payment cap for this currency.",
      "do_not": [
        "work_around_limit"
      ],
      "meaning": "The amount is above the live pilot's per-payment cap. No payment was sent.",
      "carries_existing": false
    },
    {
      "code": "rail_capability_missing",
      "type": "https://docs.perionpayments.com/problems/rail-capability-missing",
      "title": "Refund capability unavailable",
      "status": [
        409
      ],
      "retryable": true,
      "retry": "after_merchant_action",
      "who_acts": "perionpayments",
      "next_step": "Tell the merchant refunds aren't enabled for this payment's method on their account; PerionPayments enables them.",
      "do_not": [],
      "meaning": "Refunds aren't enabled for the payment method this payment used on this merchant's account. Nothing was refunded.",
      "carries_existing": false
    },
    {
      "code": "rail_unavailable",
      "type": "https://docs.perionpayments.com/problems/rail-unavailable",
      "title": "Payment rail unavailable",
      "status": [
        503
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "perionpayments",
      "next_step": "Wait for Retry-After, then try again; if a payment was already created for this order, confirm that payment again instead of creating another.",
      "do_not": [
        "create_another_payment"
      ],
      "meaning": "The payment method's provider is temporarily unavailable, so nothing was sent to it and no money moved.",
      "carries_existing": false
    },
    {
      "code": "rate_limit_unavailable",
      "type": "https://docs.perionpayments.com/problems/rate-limit-unavailable",
      "title": "Admission control unavailable",
      "status": [
        503
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "perionpayments",
      "next_step": "Wait for Retry-After, then repeat the request with the same Idempotency-Key.",
      "do_not": [
        "change_idempotency_key"
      ],
      "meaning": "Request admission was briefly unavailable, so the request wasn't accepted.",
      "carries_existing": false
    },
    {
      "code": "rate_limited",
      "type": "https://docs.perionpayments.com/problems/rate-limited",
      "title": "Rate limit exceeded",
      "status": [
        429
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "caller",
      "next_step": "Wait for Retry-After, then repeat the request with the same Idempotency-Key and body.",
      "do_not": [
        "change_idempotency_key"
      ],
      "meaning": "Too many requests arrived too quickly, so this one was refused before it ran.",
      "carries_existing": false
    },
    {
      "code": "reconciliation_list_changed",
      "type": "https://docs.perionpayments.com/problems/reconciliation-list-changed",
      "title": "Reconciliation list changed",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the list again from its first page.",
      "do_not": [],
      "meaning": "The list changed while you were paging through it. No continuation page was returned.",
      "carries_existing": false
    },
    {
      "code": "refund_exceeds_captured",
      "type": "https://docs.perionpayments.com/problems/refund-exceeds-captured",
      "title": "Refund exceeds captured amount",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the payment's refundable amount and refund no more than what remains.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The refund is more than the paid amount still available to refund. Nothing was refunded.",
      "carries_existing": false
    },
    {
      "code": "resource_missing",
      "type": "https://docs.perionpayments.com/problems/resource-missing",
      "title": "Resource not found",
      "status": [
        404
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Check the identifier, and that you are using the right merchant and environment.",
      "do_not": [],
      "meaning": "Nothing with that identifier exists for this merchant in this environment.",
      "carries_existing": false
    },
    {
      "code": "saved_card_selection_unavailable",
      "type": "https://docs.perionpayments.com/problems/saved-card-selection-unavailable",
      "title": "Saved card unavailable",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "payer",
      "next_step": "Refresh the saved cards, or let the customer enter another card.",
      "do_not": [],
      "meaning": "This saved card can no longer be used for this checkout. No new payment was sent.",
      "carries_existing": false
    },
    {
      "code": "scope_missing",
      "type": "https://docs.perionpayments.com/problems/scope-missing",
      "title": "Scope missing",
      "status": [
        403
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "merchant",
      "next_step": "Use an API client that holds the scope this operation needs; the merchant issues it.",
      "do_not": [],
      "meaning": "The token doesn't carry the scope this operation needs. Nothing was done.",
      "carries_existing": false
    },
    {
      "code": "service_unavailable",
      "type": "https://docs.perionpayments.com/problems/service-unavailable",
      "title": "Service unavailable",
      "status": [
        503
      ],
      "retryable": true,
      "retry": "after_delay",
      "who_acts": "perionpayments",
      "next_step": "Wait a short while, then repeat the request with the same Idempotency-Key.",
      "do_not": [
        "change_idempotency_key"
      ],
      "meaning": "This part of the service isn't ready right now, so the request wasn't carried out.",
      "carries_existing": false
    },
    {
      "code": "unexpected_agreement_acceptance",
      "type": "https://docs.perionpayments.com/problems/unexpected-agreement-acceptance",
      "title": "Billing agreement not present",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Remove agreement_acceptance; this checkout is a one-time payment.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "This one-time checkout doesn't create a recurring plan. Nothing was sent to the customer.",
      "carries_existing": false
    },
    {
      "code": "unsupported_api_version",
      "type": "https://docs.perionpayments.com/problems/unsupported-api-version",
      "title": "Unsupported API version",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Send Perion-Version 2026-07-15, or leave the header out.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "That API version isn't supported. Nothing was done.",
      "carries_existing": false
    },
    {
      "code": "unsupported_currency",
      "type": "https://docs.perionpayments.com/problems/unsupported-currency",
      "title": "Invalid currency",
      "status": [
        400
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Send a three-letter uppercase ISO 4217 code such as USD.",
      "do_not": [
        "repeat_unchanged"
      ],
      "meaning": "The currency isn't a valid currency code.",
      "carries_existing": false
    },
    {
      "code": "webhook_endpoint_changed",
      "type": "https://docs.perionpayments.com/problems/webhook-endpoint-changed",
      "title": "Endpoint changed after review",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Read the endpoint again, review its current settings, then send a new command.",
      "do_not": [],
      "meaning": "The webhook endpoint changed after you reviewed it. Nothing was changed.",
      "carries_existing": false
    },
    {
      "code": "webhook_endpoint_conflict",
      "type": "https://docs.perionpayments.com/problems/webhook-endpoint-conflict",
      "title": "Webhook endpoint already exists",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Use a different destination URL, or update the endpoint that already uses it.",
      "do_not": [],
      "meaning": "An endpoint in this environment already uses this URL. Nothing was created.",
      "carries_existing": false
    },
    {
      "code": "webhook_endpoint_unavailable",
      "type": "https://docs.perionpayments.com/problems/webhook-endpoint-unavailable",
      "title": "Webhook endpoint unavailable",
      "status": [
        409
      ],
      "retryable": false,
      "retry": "never",
      "who_acts": "caller",
      "next_step": "Choose an endpoint that is turned on, in this environment.",
      "do_not": [],
      "meaning": "The endpoint is turned off, deleted or in the other environment. Nothing was sent.",
      "carries_existing": false
    }
  ]
}
