# POST /api/merchants/{id}/trial/extend

Extends the specified merchant's trial.

Moves the trial end, clears the reminder marker so the new window sends its own reminders,
and returns a merchant the expiration sweep suspended to Sandbox. A merchant in any other
environment keeps it. No API key is changed: the developer's existing keys authenticate again
as soon as the extension is saved.

The new end must be later than the current time; an earlier one is refused with
`400 Merchants:TrialExtensionNotInFuture`. A locked merchant is refused as every other
write to it is.

Only a merchant on a trial can be extended. A merchant with no `PlanCode` and no
`TrialExpiresAt` is refused with `400 Merchants:TrialExtensionMerchantHasNoTrial`
and is left without a deadline.

An extension lifts only a suspension the trial caused. A merchant suspended for any other
reason is refused with `409 Merchants:TrialExtensionSuspensionNotFromTrial` and stays
suspended, with its deadline unchanged. Lift that suspension by changing the merchant's
environment, which needs the environment permission, and then extend the trial.

Changing the environment on the merchant update does not do this. A lapsed trial is refused
in every environment, so a merchant moved out of Suspended by hand still answers
`TRIAL_EXPIRED` until its trial is extended.

**Operation ID:** `merchantsExtendTrial`

## Authorization

Requires: Merchants.Merchants.ExtendTrial, merchant scope.

Required permissions:
- `Merchants.Merchants.ExtendTrial`

## Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| id | path | yes | string(uuid) | The unique identifier of the merchant whose trial to extend. |
| suppressNulls | query | no | boolean | If true, omit properties with null values. |

## Request Body

**Content type:** `application/json`

Schema: `ExtendMerchantTrialDto`

Properties:
- `trialExpiresAt` (string(date-time)) required: The new trial end, UTC. Must be later than the current time: an earlier value is refused  with `400 Merchants:TrialExtensionNotInFuture` and nothing is changed.

_Example: Extend a suspended trial_

Moves the trial end to 31 January 2030 (UTC). A merchant the expiration sweep suspended is returned to Sandbox, the reminder marker is cleared so the new window sends its own reminders, and the developer's existing API keys authenticate again immediately.

```json
{
  "trialExpiresAt": "2030-01-31"
}
```

**Content type:** `text/json`

Schema: `ExtendMerchantTrialDto`

Properties:
- `trialExpiresAt` (string(date-time)) required: The new trial end, UTC. Must be later than the current time: an earlier value is refused  with `400 Merchants:TrialExtensionNotInFuture` and nothing is changed.

_Example: Extend a suspended trial_

Moves the trial end to 31 January 2030 (UTC). A merchant the expiration sweep suspended is returned to Sandbox, the reminder marker is cleared so the new window sends its own reminders, and the developer's existing API keys authenticate again immediately.

```json
{
  "trialExpiresAt": "2030-01-31"
}
```

**Content type:** `application/*+json`

Schema: `ExtendMerchantTrialDto`

Properties:
- `trialExpiresAt` (string(date-time)) required: The new trial end, UTC. Must be later than the current time: an earlier value is refused  with `400 Merchants:TrialExtensionNotInFuture` and nothing is changed.

_Example: Extend a suspended trial_

Moves the trial end to 31 January 2030 (UTC). A merchant the expiration sweep suspended is returned to Sandbox, the reminder marker is cleared so the new window sends its own reminders, and the developer's existing API keys authenticate again immediately.

```json
{
  "trialExpiresAt": "2030-01-31"
}
```

## Responses

### 200

OK

**Content type:** `application/json`

Schema: `MerchantDto`

Properties:
- `extraProperties` (object)
- `id` (string(uuid))
- `creationTime` (string(date-time)): The date and time when this entity was created.
- `creatorId` (string(uuid)): The ID of the user who created this entity.
- `lastModificationTime` (string(date-time)): The date and time when this entity was last modified.
- `lastModifierId` (string(uuid)): The ID of the user who last modified this entity.
- `isDeleted` (boolean): Indicates whether this entity has been deleted.
- `deleterId` (string(uuid)): The ID of the user who deleted this entity, if it is deleted.
- `deletionTime` (string(date-time)): The date and time when this entity was deleted, if it is deleted.
- `createdFromTemplateId` (string(uuid)): The template this record was created from, or `null` for one started blank. Set by the  create-from-template path and by the add/edit page's Load Template action when the record is  saved.
- `name` (string): Gets or sets the display name of the merchant.
- `concurrencyStamp` (string): Gets or sets the concurrency stamp used for optimistic concurrency control.
- `tenantId` (string(uuid)): Gets the tenant identifier for multi-tenancy isolation.
- `resellerId` (string(uuid)): Gets or sets the unique identifier of the reseller that owns this merchant.
- `resellerName` (string): Gets or sets the display name of the owning reseller.
- `entityVersion` (integer(int32)): Gets the entity version number, incremented on each update for optimistic concurrency.
- `customIdentifier` (string): Gets or sets an optional custom identifier assigned to the merchant by the reseller or integrator.
- `dba` (string): Gets or sets the "Doing Business As" (DBA) name for the merchant.
- `legacyNumber` (integer(int64)): Gets the merchant's legacy numeric key, the integer identifier the v1 API addresses merchants  by (its `MerchantKey`). Server-owned and stamped once at create; null on merchants  written before the field existed until the v1 data migration back-fills them.
- `mcc` (string): Gets or sets the four-character ISO 18245 Merchant Category Code identifying the merchant's industry.  Null on legacy merchants that have not yet been backfilled; the merchant grid and detail view  surface a warning indicator in that case so internal staff can address it.
- `isTest` (boolean): Gets or sets a value indicating whether this merchant is a test account used for non-production transactions.
- `environment` (object): Gets or sets the merchant's lifecycle environment.
- `planCode` (string): Gets or sets the plan this merchant is on, or `null` when it is on none.
- `trialExpiresAt` (string(date-time)): Gets or sets when this merchant's trial ends, UTC, or `null` when it is not on  a time-limited plan.
- `trialExpiryWarnedThresholdDays` (integer(int32)): Gets or sets the most recent trial expiry reminder sent in the current trial window, as the  number of days before `trialExpiresAt` it was sent at.
- `processorMode` (object): Gets or sets which processor a sandbox merchant's transactions route to.
- `isActive` (boolean): Gets or sets a value indicating whether the merchant is currently active and able to process transactions.
- `contactDetail` (object): Gets or sets the merchant's contact details including addresses, phone numbers, and email.
- `businessInfo` (object): Gets or sets the merchant's business information such as currency, tax IDs, and industry codes.
- `virtualTerminal` (object): Gets or sets the virtual terminal field configuration for the merchant.
- `processing` (object): Gets or sets the processing settings including duplicate checks, card verification, and processor profiles.
- `features` (object): Gets or sets the feature flags and capability settings for the merchant.
- `capabilities` (object): Gets or sets the read-only, server-computed capability flags derived from the  merchant's configuration (e.g., active processor profiles' enabled tenders).  Intended for UI affordance decisions; not enforced at submit time.  Populated by the AutoMapper profile on read; not accepted on Create/Update.
- `customFields` (array<CustomField>): Gets or sets the collection of custom fields defined for the merchant.
- `branding` (object): Gets or sets the merchant-level branding configuration for receipts and customer-facing communications.
- `accountUpdater` (object): Gets or sets the account updater configuration for this merchant.
- `merchantFlowConfig` (object): Gets or sets the optional transaction flow configuration that controls how transactions are orchestrated for this merchant.
- `billingAssignment` (object): Gets or sets the billing plan assignment for this merchant. A null value means the  merchant has no active billing plan and will be skipped by billing runs.
- `notes` (array<EntityNote>): Gets or sets operational notes attached to the merchant.
- `saveWarnings` (array<MerchantSaveWarning>): Non-blocking informational warnings produced by the most recent create/update of this  merchant (e.g. international-AVS-on-US-only-surface advisories).  `null` on read responses, populated (possibly with an empty list) on  create/update responses, so clients can distinguish "this isn't a save response"  (omitted from JSON via `WhenWritingDefault`) from  "saved successfully with no warnings" (empty array). Not persisted.
- `volumeTrend` (array<number(double)>): Metered transaction count per calendar month for this merchant, oldest to newest, as an  activity trend. `null` on every response that does not populate it (which is all of them  except the merchant list), so it is omitted from JSON entirely rather than serialized as  null. Not persisted.
- `digitalWallets` (array<MerchantDigitalWallet>): Gets or sets the per-merchant digital wallet bindings (Apple Pay, Google Pay, …).  At most one entry per `WalletProviderType`; each carries the master enable  flag and a pointer to the `WalletProviderRegistration` row this merchant uses  (platform-level or merchant-level).
- `threeDSBindings` (array<MerchantThreeDSBinding>): Gets or sets the per-merchant 3-D Secure provider bindings. At most one entry per  `ThreeDSProviderType`; each carries the master enable flag, the policy mode, and the  vendor credentials.                   <b>The JWT secret is never populated on a read.</b> It is stripped server-side before this  DTO leaves the application service, so a caller sees null there whether or not one is  stored. Sending null or an empty string back on a save keeps the stored secret; sending a  value replaces it.
- `taxBindings` (array<MerchantTaxBinding>): Gets or sets the per-merchant tax provider bindings. At most one entry per provider name; each  carries the master enable flag, whose provider account the lookups are billed to, and the  provider's configuration values.                   <b>Secret field values are never populated on a read.</b> They are stripped server-side before this  DTO leaves the application service, and each field reports `isConfigured` instead so an  operator can tell a stored secret from an empty one. Sending null or an empty string back on a save  keeps the stored secret; sending a value replaces it.                     A binding whose credential source is `Gateway` carries no values at all: the gateway's own  provider credentials are never copied onto a merchant, and are resolved at lookup time instead.
- `shippingBindings` (array<MerchantShippingBinding>): Gets or sets the per-merchant shipping rate provider bindings. At most one entry per provider name;  each carries the master enable flag, whose provider account the quotes are billed to, and the  provider's configuration values.                   <b>Secret field values are never populated on a read.</b> They are stripped server-side before this  DTO leaves the application service, and each field reports `isConfigured` instead so an  operator can tell a stored secret from an empty one. Sending null or an empty string back on a save  keeps the stored secret; sending a value replaces it.                     A binding whose credential source is `Gateway` carries no values at all: the gateway's own  provider credentials are never copied onto a merchant, and are resolved at quote time instead.
- `paymentEncryptionBindings` (array<MerchantPaymentEncryptionBinding>): Gets or sets the per-merchant payment encryption provider bindings. At most one entry per provider  name; each carries the master enable flag, whose provider account the decryption calls are billed  to, the provider's configuration values, and the key serial identifier table.                   <b>Secret field values and key references are never populated on a read.</b> They are stripped  server-side before this DTO leaves the application service. Each field reports  `isConfigured` and each key entry reports `isKeyReferenceConfigured` in their place, so  an operator can tell a stored value from an empty one. Sending null or an empty string back on a  save keeps the stored value; sending a value replaces it.                     A binding whose credential source is `Gateway` carries no field values at all: the gateway's  own provider credentials are never copied onto a merchant, and are resolved at decryption time  instead. Key entries are the merchant's either way, because a key serial identifier maps that  merchant's own device fleet.
- `promotedAt` (string(date-time)): When this merchant was promoted to `Production` through the  promotion action, UTC. Null for a merchant that has never been promoted that way.
- `promotedByUserId` (string(uuid)): The user who ran the promotion that stamped `promotedAt`, or null when the  merchant has never been promoted through that action.
- `isLocked` (boolean)
- `lockedAt` (string(date-time))
- `lockedByUserId` (string(uuid))
- `lockedByUserName` (string)
- `lockReason` (string)

### 403

Forbidden

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 401

Unauthorized

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 400

Bad Request

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 404

Not Found

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 501

Not Implemented

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 500

Internal Server Error

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### default

The request failed. The body carries the standard error envelope: a machine-readable `error.code`, a human-readable `error.message`, and `error.validationErrors` when the failure was a validation rejection. See the error-code reference in this document's description for the values `error.code` can take.

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 429

The request was refused because a rate limit was exceeded, or because something a later retry can clear stopped it. A rate limit refusal carries an `application/problem+json` body: wait at least the interval `Retry-After` names before retrying, then back off. Limits are tuned per deployment, so read the allowance from the response headers rather than assuming a fixed ceiling. Any other refusal carries the standard error envelope as `application/json`, and its `error.code` names the cause.

**Content type:** `application/problem+json`

Schema: `RateLimitProblemDetails`

Properties:
- `type` (string) required: The problem type identifier. Always the same value: the failure is the status code itself,  so there is no sub-type for a caller to branch on.
- `title` (string) required: A short, human-readable summary of the problem type.
- `status` (integer(int32)) required: The HTTP status code, repeated in the body as the problem-details format defines.
- `detail` (string) required: A human-readable explanation of this occurrence of the problem.
- `retryAfterSeconds` (integer(int32)) required: How long to wait before retrying, in whole seconds, carrying the same figure as the  `Retry-After` header. Always at least one: a value of zero would invite an immediate  retry that is certain to be rejected again.

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

## Example request

Every block below sends the same request. Replace {{BASE_URL}} with the address of the API you are calling and {{API_KEY}} with your own key.

The request body is a ExtendMerchantTrialDto. See the Request body section below for its fields.

### cURL

```bash
curl -X POST "{{BASE_URL}}/api/merchants/{id}/trial/extend" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "trialExpiresAt": "2030-01-31"
}'
```

### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "trialExpiresAt": "2030-01-31"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/merchants/{id}/trial/extend' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, MerchantsApi } from '@winkpg/winkpg-api';

const api = new MerchantsApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.merchantsExtendTrial("3fa85f64-5717-4562-b3fc-2c963f66afa6", {
  "trialExpiresAt": "2030-01-31"
});
```

### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/merchants/{id}/trial/extend', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "trialExpiresAt": "2030-01-31"
  }),
});

const data = await response.json();
```

### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new MerchantsApi(config);
var body = JsonSerializer.Deserialize<ExtendMerchantTrialDto>("""
    {
      "trialExpiresAt": "2030-01-31"
    }
    """);

var result = await api.MerchantsExtendTrialAsync(Guid.Parse("3fa85f64-5717-4562-b3fc-2c963f66afa6"), body);
```

### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/merchants/{id}/trial/extend");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "trialExpiresAt": "2030-01-31"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.MerchantsApi(client)
    body = winkpg_api.ExtendMerchantTrialDto.from_dict({
      "trialExpiresAt": "2030-01-31"
    })
    result = api.merchants_extend_trial("3fa85f64-5717-4562-b3fc-2c963f66afa6", body)
```

### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "trialExpiresAt": "2030-01-31"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/merchants/{id}/trial/extend",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

## See also

- [All documentation](https://devportal-simpay-sbx.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
