---
title: "Connect an integration"
method: POST
path: "/organizations/{id_organization}/integrations"
tags: ["integrations"]
---

# Connect an integration

`POST /organizations/{id_organization}/integrations`

## Request

- Base URL: `https://api.planvortex.com/v1.0.0`
- URL: `https://api.planvortex.com/v1.0.0/organizations/{id_organization}/integrations`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (client credentials, token URL `https://api.planvortex.com/v1.0.0/oauth/token`)

Creates the connection. The body depends on the provider:

- `google_drive`: `{ provider, code }`, where `code` is the OAuth code returned to the redirect of `connect_link`. It is single-use, so the plan allowance is checked **before** the exchange.
- `rss`: `{ provider, url, id_accounts, template?, publication_type?, auto_publish?, import_image? }`. The feed is read once to validate it and to take its title, and every item it already has is recorded as seen — connecting a blog never publishes its back catalogue.
- `woocommerce`: `{ provider, url, consumer_key, consumer_secret }`, a **read** key created by hand in the store. The keys are tried against the store before anything is saved, and an organization can connect several stores: each one takes one integration of the allowance. The approval button (`connect_link`) does the same without anyone copying a key, and is the better way whenever the store allows it.

Requires the `integrations:create` permission.

## Path parameters

- `id_organization` string, required

## Request body

- union
  - IntegrationsGoogleDriveConnectRequest
    - `provider` 'google_drive', required
    - `code` string, required: OAuth code returned to the redirect of connect_link. Single-use.
  - IntegrationsRssConnectRequest
    - `provider` 'rss', required
    - `url` string, required: Public feed URL (RSS 2.0 or Atom). Private or authenticated feeds are not supported.
    - `id_accounts` string[], required: Accounts of this organization the entries will be published to. At least one, otherwise 2206.
    - `publication_type` string: Optional; defaults to `"profile"`.
    - `template` string: Text template. Placeholders: {{title}}, {{link}}, {{summary}}. The result is truncated to the character limit of each network, taken from GET /social_limits. Optional; defaults to `"{{title}}\n\n{{link}}"`.
    - `auto_publish` boolean: false (the default) creates each entry as a draft for review. true schedules it a few minutes out, so there is a window to catch it before it goes out on the client's networks. Optional; defaults to `false`.
    - `import_image` boolean: Import the entry's featured image (enclosure, media:content or the first <img> of the content) into the library and attach it. Optional; defaults to `true`.
  - IntegrationsWooCommerceConnectRequest
    - `provider` 'woocommerce', required
    - `url` string, required: The store address. **https only**: a store that only answers on http is not connected, because the key would travel in the clear. Its redirects (http to https, bare domain to www) are resolved now, without credentials, and the final URL is what gets stored.
    - `consumer_key` string, required: Starts with `ck_`. Created in WooCommerce → Settings → Advanced → REST API with **Read** permission.
    - `consumer_secret` string, required: Starts with `cs_`. Stored encrypted and never returned by any endpoint.

## Response `200`

Integration connected

- object
  - `integration` IntegrationsIntegration, required: Credentials are never returned. `connected` is the summary the panel paints: false means the connection failed and needs attention.
    - `_id` string, required
    - `id_organization` string, required
    - `id_client` string, required
    - `provider` 'google_drive' | 'rss' | 'woocommerce', required
    - `name` string, required: The Google account, the feed title, or the store's site name.
    - `external_identifier` string: The Google account email, the feed URL, or the store URL.
    - `config` IntegrationsIntegrationConfig, required: What `config` holds, per provider. `google_drive`: nothing. `rss`: the feed settings. `woocommerce`: how to reach the store, which the server detects when connecting. None of it is a secret.
      - `url` string: `rss`: the feed. `woocommerce`: the store, as it was resolved when connecting (after its own redirects, which from then on are never followed). **A store's `url` cannot be changed with `PUT`** (`2220`): it decides which server receives the stored key. Changing store is reconnecting.
      - `id_accounts` string[]: `rss`.
      - `publication_type` string: `rss`.
      - `template` string: `rss`.
      - `auto_publish` boolean: `rss`.
      - `import_image` boolean: `rss`.
      - `seen_guids` string[]: `rss`. Entries already processed (last 200, FIFO). Owned by the job: filled at connection time with everything the feed already had, so the back catalogue is never published.
      - `last_checked` string, date-time: `rss`.
      - `api_base` 'wp-json' | 'rest_route': `woocommerce`. Where the store's REST API answers: `wp-json` (`/wp-json/wc/v3/…`) or `rest_route` (`/?rest_route=/wc/v3/…`), the fallback for stores with plain permalinks. A `rest_route` store cannot use the approval button (`2216`).
      - `auth_mode` 'basic' | 'query': `woocommerce`. How the key travels: `basic` (the `Authorization` header) or `query` (in the URL, only for hostings that drop that header). Detected when connecting.
      - `key_ending` string: `woocommerce`. The last 7 characters of the consumer key, which is what WooCommerce shows in its key list (WooCommerce → Settings → Advanced → REST API). Disconnecting cannot revoke the key: this is how the user finds which one to delete there.
      - `currency` string: `woocommerce`. The store currency, when the key could read it (it needs a shop manager's key). Only used for a product that comes without `price_html`.
      - `tax_location_missing` boolean: `woocommerce`. **true when the store's API gives prices WITHOUT tax, labelled as tax included.** It happens with the default customer location set to geolocate (or to no location), prices entered without tax and tax based on the customer's address: an API request has no customer, so WooCommerce has nowhere to charge tax, and `price_html` reads "20,00 € IVA incluido" where the shop charges 24,20 €. While it is true, the store's **taxable products come back without a price** rather than with a wrong one; products with no tax keep theirs. It is checked when connecting, so after fixing the setting (WooCommerce → Settings → General → Default customer location → Shop country/region) the store has to be reconnected. Always present on a store, also as `false`.
    - `enabled` boolean, required: Only enabled integrations consume plan allowance.
    - `connected` boolean, required: `error_code` is empty. It is computed on the way out, not stored: the panel needs to know whether the connection is alive, not with which credentials.
    - `error_code` integer, nullable: PlanVortex error code of the last failure (2203 token revoked, 2205 feed unreachable, 2211 the store rejected its key, 924 no publication allowance left…). **2219 is not a failure**: a store connected with the button is being checked, for a few seconds; if it stays there, the check never finished and the store has to be reconnected.
    - `last_used_date` string, date-time
    - `creation_date` string, date-time, required

## Other responses

- `400`: The request failed. The body carries the PlanVortex error code in `code`: | Code | Meaning | | --- | --- | | `1404` | Integrations limit reached for the organization plan. On the free plan the allowance is 0, so this always fires. | | `2201` | Integration provider not supported (or the payload is missing what that provider needs). | | `2202` | Integration already connected for this organization and provider. Applies to OAuth providers; the body carries its `id_integration` in `data`, and the `code` you sent is still unused — post it to that integration's `/reconnect` to renew it. | | `2203` | Integration token expired or revoked, reconnect required. | | `2205` | Feed URL unreachable or not a valid feed. | | `2206` | Feed has no target accounts configured. | | `2210` | The store URL cannot be used. `data.reason`: `invalid_url`, `not_https` (the store only answers on http, and a key never travels in the clear), `non_public_address`, `redirect` (the store redirected a request carrying credentials, which is never followed) or `too_many_redirects`. | | `2211` | The store rejected the API key: it does not exist, it was deleted in WordPress, the secret does not match, or it has no read permission. `data` carries what the store said. | | `2212` | A firewall or a security plugin in front of the store answered instead of WooCommerce (an HTML challenge from Cloudflare, Wordfence…). **It is not the keys**: the store's hosting has to let PlanVortex's server through. | | `2213` | The store could not be reached, timed out or failed on its side (a 5xx). | | `2214` | There is no WooCommerce REST API at this URL: not a WordPress, WooCommerce not active, or the REST API disabled. | | `2215` | The keys are missing or do not have WooCommerce's shape: the consumer key starts with `ck_` and the secret with `cs_`. Pasting them the other way round is the usual mistake. |

---

[API](https://skmtc.dev/planvortex/apis/planvortex-api.md) · [All operations](https://skmtc.dev/planvortex/apis/planvortex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/planvortex/apis/planvortex-api/revisions/abedbbc1cd52?raw)
