Skip to main content

Create subscription

METHODURL
POST/subscriptions
EXAMPLEhttps://test.api.revuppayments.com/v2/subscriptions

The call to the subscriptions API call has a similar body, response and authentication as the create order.
These are the differences:

Body differences​

  • money object: It does not have to be sent because it is defined in the plan

  • currency: New field. Mandatory. This field allows multicurrency for a plan. Currency in which you want the recurrences to be charged.

  • generateToken: It does not have to be sent because it will be always generated

  • notificationUrl: It does not have to be sent because it is replaced by eventSubscriptionUrl

  • parentTransactionId: It does not have to be sent because Revup is going to generate the rebills

  • planId: New field. It must be sent. This is the ID from the previously created Plan in the BO. UUID format

    paymentDetails object: It does not have to be sent because it is not necessary. (Note: Mandatory for recurrencies with ApplePay and MPAN)

  • eventSubscriptionUrl: New field. It must be sent. This is the URL where Merchant will receive subscription events

  • overwrite.initialAmount: New field. Optional. If present, the initial subscription amount will be overwritten by the specified amount.

Response differences​

  • subscriptionId: New field. This is the ID from the new subscription created. UUID format

Request body example - Create new subscription​

{
"planId": "4aa7c1e4-cf8c-4d1c-a6ad-451dd07411ee",
"currency": "EUR",
"eventSubscriptionUrl": "https://2ec7-54-73-197-22.ngrok-free.app/events",
"description": "New subscription from merchant",
"concept": "Order concept",
"redirectUrl": {
"success": "www.merchant-domain.es/success",
"fail": "www.merchant-domain.es/failed",
"threeDSChallengeResult": "www.merchant-domain/challenge"
},
"userInformation": {
"ip": "84.102.122.9",
"email": "user@test.com",
"country": "ES",
"language": "ES",
"customerId": "12345678901234567890",
"browserInformation": {
"userAgentHeader": "Mozilla/5.0",
"acceptHeader": "text/html",
"fingerprint": "4sd54dfsafgsadf5"
}
},
"overwrite": {
"initialAmount": 111.99
},
"domain": "https://www.merchant-domain.es"
}

Success responses - Create subscription​

HTTP CODEDESCRIPTION
200The orderId and subscriptionId are provided in the body in JSON format.

Success response example - Create subscription​

{
"orderId": "2989ce95-4309-4f24-a81a-f8dc27c9f20d",
"subscriptionId": "0192b445-55a7-74af-a5d1-79ca6d2310f4"
}

Error responses - Create subscription​

HTTP CODEERROR CODEERROR MESSAGEDESCRIPTION
4000905Problem Parsing RequestThe request syntax is incorrect.
4000300Invalid RequestThe request body does not match the JSON schema.
4000301Missing or invalid field: %sMandatory or format field error.
4000313Invalid CountryThe provided country is not valid. The code has to comply with ISO 3166.
4000705Invalid Format ProvidedThe provided language is not valid. The code has to comply with ISO 639-1.
4000605Invalid IP AddressThe provided IP is not valid. The IP has to comply with the IPv4 protocol and be a public address.
4000203Duplicated SubscriptionThe provided purchase ID is already being used in another subscription or transaction with status pending or success.
5000900Internal Server ErrorThe server experienced an unexpected error.

Subscription creation: idempotency keys​

A network timeout leaves you not knowing whether the subscription was created. Retrying blindly can charge your customer twice. The x-idempotency-key header removes that risk.

Without it, the same request sent twice creates two subscriptions and charges twice.

How to use it​

  1. Generate a key before your first attempt. UUID v7 is recommended; any canonical UUID is accepted.
  2. Send it on the request.
  3. On a network failure or timeout, retry with the same key.
  4. Use a new key for every new operation — and whenever you change the body.
POST /subscriptions HTTP/1.1
x-api-key: <your key>
x-idempotency-key: 0192f3a1-8b7c-7def-8123-456789abcdef
Content-Type: application/json

{
"planId": "3f1c9a52-8b0e-4d77-9f31-6c2a5e8d4b10",
"eventSubscriptionUrl": "https://your-domain.example/webhooks/subscriptions",
"tokenId": "tok_8f2c41a9",
"parentOrderId": "ORD-2026-000184",
"parentTransactionId": "9c1b7e40-3a52-4d18-bb6f-2e7c05a91d33"
}

Retrying that exact request with that exact key returns the same subscriptionId and does not charge the card a second time.

The header​

Namex-idempotency-key
RequiredNo
FormatA canonical UUID: 8-4-4-4-12 hexadecimal digits. v7 recommended.
ScopeYour account. Two merchants can use the same value without interfering.
Lifetime24 hours from the first request that used it 

Every response to a request that carried the header echoes it back in x-idempotency-key.

What happens​

Situation  Response
First request with a keyRuns normally
Retry, same key, same bodyThe original response, replayed exactly — same subscriptionId, no second charge
Retry while the first is still in flight409 — back off and retry the same key
Same key, different body422 — do not retry; the key belongs to the first request
Malformed or empty key400
Idempotency service unavailable503- retry the same key

Deliberate upsell vs. accidental retry​

This is what the key is really for. Creating two subscriptions from the same parent payment is a legitimate operation, and without a key we cannot tell it apart from a retry:

  same parentOrderId, two DIFFERENT keys   →  two subscriptions   (deliberate upsell)
same parentOrderId, the SAME key → one subscription (retry, replayed)
same parentOrderId, NO key → two subscriptions (we cannot tell)

Four things worth knowing​

Keys live 24 hours. After that the key is forgotten, and reusing it starts a brand new operation — which will create another subscription.

The body is compared, not just the key. Two bodies that mean the same thing match even if the JSON keys are in a different order, the indentation differs, or an amount is written 10.00 instead of 10.0. Any real difference is a 422.

A 400 consumes the key. If your request fails validation, that 400 is stored against the key. Fixing the body and retrying with the same key returns 422, not a fresh attempt. Always use a new key when you change the body.

A replayed 200 still does not mean the payment succeeded. A 200 means the subscription was accepted; the charge happens afterwards. You learn its outcome from the event delivered to your eventSubscriptionUrl, exactly as without the header.

Error codes - Idempotency keys​

HTTPCodeWhenWhat to do
400 0060The key is not a canonical UUIDFix the key format 
409 0061The same key is being processed right now Back off, retry the same key
422 0062The key was already used with a different requestDo not retry; use a new key
500 0063A previous attempt with this key never completed; its outcome is unknownContact support before retrying
503 0064The idempotency service is unavailableRetry the same key

All errors share one shape:

{
"error": {
"code": "0062",
"description": "This idempotency key was already used with a different request"
}
}

Checklist​

  • Generate one key per logical operation, before the first attempt.
  • Retry timeouts with the same key; never with a new one.
  • Use a new key whenever the body changes.
  • Do not retry a 422.
  • Back off on 409, then retry the same key.
  • Use two different keys when you deliberately want two subscriptions.
  • Keep waiting for the webhook to confirm payment — a replayed 200 is not a receipt.