# Save a Route draft

`POST /v2/routes/{id}/draft`

Core API v2, Routes.

Save an unfinished edit of the decision tree, to come back to later. A Route has at most one draft: this creates it, or replaces its tree. A draft is not a version. No payment is routed by it, and it cannot be activated until you publish it. Each save gives the draft a higher `revision`, and a Route never gives the same `revision` to two drafts. Send the `revision` you edited from, or `0` when the Route had no draft, and a save made after someone else's is refused instead of replacing their tree.

**Authentication:** Send `Authorization: Bearer <token>`. To authenticate, generate a Secret API Key from your ODUS Dashboard and include it in the `Authorization` header as follows: `Authorization: Bearer <YOUR_SECRET_API_KEY>`

## Parameters

### Path parameters

- `id` (string, required)

## Request body

Content type `application/json`, required.

Type: object (SaveRouteDraftRequestDTO).

- `revision` (integer): The `revision` of the draft you edited this tree from, or `0` if the Route had no draft when you started. When you send it and the draft's current `revision` is different, the Route has no draft, or you sent `0` and the Route now has a draft, the save is refused with `409` and the draft is left unchanged. Leave it out to save the tree whatever the draft's revision. Minimum 0. Maximum 9007199254740991.
- `tree` (object or object or object or object, required): The decision tree as edited so far, shaped as a published tree. It may be incomplete: whether it is complete and correct is checked only when the draft is published. Each save replaces the whole tree.
  - When the value is variant 1:
    - `type` (string, required): One of `condition`.
    - `branches` (array of object): Tested in order. The payment follows the first branch whose condition matches. A decline inside that branch does not move on to a later branch. Maximum items 20.
      - `when` (object or object or object or object): A test on the payment: a single comparison, or `all`, `any` or `not` over other conditions. A fact the payment does not have makes the comparison unknown, never false, and an unknown condition does not match.
        - When the value is variant 1:
          - `all` (array of (object or object or object or object), required)
            - Same fields as `RouteDraftConditionDTO`, described above.
        - When the value is variant 2:
          - `any` (array of (object or object or object or object), required)
            - Same fields as `RouteDraftConditionDTO`, described above.
        - When the value is variant 3:
          - `not` (object or object or object or object, required): A test on the payment: a single comparison, or `all`, `any` or `not` over other conditions. A fact the payment does not have makes the comparison unknown, never false, and an unknown condition does not match.
            - Same fields as `RouteDraftConditionDTO`, described above.
        - When the value is variant 4:
          - `field` (string): The payment fact to test: `card.brand`, `card.bin`, `card.issuerName`, `card.issuerCountry`, `card.fundingType`, `amount`, `currency`, `initiator`, `statementDescriptor`, `statementDescriptorSuffix`, or `metadata.<key>`. Minimum length 1. Example: `card.issuerCountry`.
          - `op` (string): How the fact is compared with `value`. Each field supports its own operators. One of `eq`, `ne`, `in`, `nin`, `gt`, `gte`, `lt`, `lte`, `contains`, `startsWith`, `exists`.
          - `value` (string or integer or array of string): What the fact is compared with: a list for `in` and `nin`; a prefix or a list of prefixes for `startsWith`; a number in minor units for `amount`; omitted for `exists`. Example: `US`.
      - `then` (object or object or object or object): One step of a draft route: a `condition`, a `split`, a `pool` or a `gateway`, shaped as in a published route. Every field except `type` may be left out, and any list may be empty, until the draft is published.
        - Same fields as `RouteDraftNodeDTO`, described above.
    - `else` (object or object or object or object): Followed when no branch matches. Without it, the route ends there.
      - Same fields as `RouteDraftNodeDTO`, described above.
  - When the value is variant 2:
    - `type` (string, required): One of `split`.
    - `outputs` (array of object): Each payment follows exactly one output, chosen in proportion to `percent`. The same payment follows the same output each time it reaches this split. A decline inside that output does not move on to another output. Maximum items 20.
      - `percent` (integer): The share of payments that follow this output, as a whole percentage. The percentages of one split add up to 100. Minimum 1. Maximum 99. Example: `20`.
      - `then` (object or object or object or object): One step of a draft route: a `condition`, a `split`, a `pool` or a `gateway`, shaped as in a published route. Every field except `type` may be left out, and any list may be empty, until the draft is published.
        - Same fields as `RouteDraftNodeDTO`, described above.
  - When the value is variant 3:
    - `type` (string, required): One of `pool`.
    - `strategy` (string): The order the children are tried in. `ordered` keeps the written order; `roundRobin` starts at a position that moves on from one payment to the next; `weighted` picks the order at random, in proportion to `weights`. One of `ordered`, `roundRobin`, `weighted`.
    - `children` (array of (object or object or object or object)): Tried in turn. When a child declines, the pool fails over to the next one; a child that is not attempted, such as an inactive gateway, is passed over. Maximum items 20.
      - Same fields as `RouteDraftNodeDTO`, described above.
    - `weights` (array of integer): One weight per child, for a `weighted` pool only. Maximum items 20. Each item: Minimum 1. Maximum 100.
    - `maxAttempts` (integer): The most gateway attempts this pool may make, including those of its children. Minimum 1. Maximum 11.
  - When the value is variant 4:
    - `type` (string, required): One of `gateway`.
    - `gatewayProfileId` (string): The gateway profile to attempt. It must be active and support card payments when the version is published. Minimum length 1. Example: `gwp_2mXGTxXIkqv4uSdbm8DJCzvHUIu`.

## Responses

### `200`

OK

Type: object (RouteDraftDTO).

- `createdAt` (string (date-time), required): The date and time when the entity was created. Example: `2024-02-18T12:00:00Z`.
- `updatedAt` (string (date-time), required, nullable): The date and time when the entity was last updated. Example: `2024-02-21T12:15:00Z`.
- `metadata` (map of string to string, nullable): Metadata used by merchants to store additional information about the entity.
- `id` (string, required): The unique identifier of the draft. Example: `rtd_2mXGTxXIkqv4uSdbm8DJCzvHUIu`.
- `route` (string, required): The ID of the route this is a draft of. Example: `rte_2mXGTxXIkqv4uSdbm8DJCzvHUIu`.
- `tree` (object, required): The decision tree exactly as it was last saved. It may be incomplete, and no payment is ever routed by it.
- `revision` (integer, required): The revision of the tree. It goes up each time a save creates or replaces the tree, and a Route never gives the same revision to two drafts, so a draft created after another was published or deleted has a higher revision than it. Send it back as `revision` when you save or publish the draft, so the request is refused if someone else saved, published or deleted the draft in the meantime. Minimum -9007199254740991. Maximum 9007199254740991. Example: `3`.

### `202`

The merchant is entitled but its environment is not provisioned yet. Provisioning has been kicked off (exactly once) and is in progress; retry the request — it succeeds once the environment is ready. Returned only for identity-token (dashboard) requests bound to a merchant, not for secret-key API calls; any such endpoint can return it while provisioning is underway.

Type: object (ProvisioningInProgress).

- `status` (string, required): Signals the merchant environment is still being provisioned. One of `provisioning`.
- `merchantAccountId` (string, required): The merchant account whose environment is being provisioned. Example: `dAI3aa87ROOeupeBCjNSKCOBOisBCYjB`.

### Errors

- `400`: The request was rejected. `type` is `invalid_request_error` when the request itself is at fault — `errors` then lists every problem found, with field-attributable entries prefixed by the field’s path; or `invalid_state_error` when the request was well-formed but the resource is not in a state that allows it.
- `401`: No API key was supplied, or the key is not valid. `type` is `authentication_error`.
- `403`: The API key is valid but lacks the permission this operation requires. `type` is `permission_error`.
- `404`: No resource exists with the requested identifier. `type` is `not_found_error`.
- `409`: `type` is `conflict_error`. The supplied `X-Idempotency-Key` was already used with a different request body (`code` is `idempotency_conflict`, and retrying will not help), or the resource is being modified by another in-flight request (`code` is `resource_locked`, and retrying with backoff will). It is also answered when you send `revision` and the draft is at a different revision, the Route has no draft, or you sent `0` and the Route has a draft (`code` is `stale_revision`). The draft is left unchanged. Retrying will not help: read the draft again, then save against its current `revision`.
- `429`: Too many requests. A limit applies per operation; requests to other operations are unaffected. `type` is `rate_limit_error`. Use the `Retry-After-global` header to know when to retry.
- `500`: The request could not be completed because of an unexpected error. `type` is `api_error`.
- `503`: The request could not be served right now: the API is temporarily at capacity, or a dependency needed to authorize the request is unavailable — the auth service that verifies credentials, or the entitlement lookup behind it. `type` is `api_error`. It is raised before the operation runs, so the request had no effect, and unlike a plain 500 the condition is transient: retry with backoff.
- `504`: The request exceeded the processing time limit and was abandoned. `type` is `api_error` and `code` is `timeout` — unlike a plain 500 the request may still have taken effect, so retry with the same idempotency key rather than blindly.

Every error response has the same body: see [Errors](https://docs.odus.com/docs/guides/api-features/errors.md).

---

Generated from the Core API v2 OpenAPI document, https://api.odus.com/openapi.v2.json. Interactive page: https://docs.odus.com/docs/api/core/v2/route-controller-save-draft
