Skip to content

WordPress plugins and AI agents

The Partner API is free, and software can reach it without anyone registering it by hand: a WordPress booking plugin installed on many salons’ sites, a website an AI builder generated, or an AI agent that books for a business. Each copy registers itself, then the business connects it on the United Pet Club consent page like any other app.

This page walks through the whole flow. Authentication is the reference for every field and error.

You needWhy
Nothing from United Pet ClubRegistration is anonymous: no account, no key, no card.
A redirect URIThe page of your site or app the business’s browser comes back to: https, or http on localhost, 127.0.0.1 or [::1] while you develop.
A business on United Pet ClubSomeone who acts as the business’s profile signs in and allows your app.
A payment card on the business’s accountConnecting is free, but a business connects apps only while it keeps a valid payment card on file (Dashboard → Finances → Billing).
StepCallWho, and how often
1. DiscoverGET https://api.unitedpetclub.com/.well-known/oauth-protected-resource, then GET https://auth.unitedpetclub.com/.well-known/oauth-authorization-serverYour software; optional if you use the hosts on this page
2. RegisterPOST https://auth.unitedpetclub.com/connect/registerYour software, once per site or install
3. ConnectThe business’s browser goes to https://auth.unitedpetclub.com/connect/authorize with a PKCE challengeThe business, once
4. ExchangePOST https://auth.unitedpetclub.com/connect/token with grant_type=authorization_codeYour server, once per connection
5. Callhttps://api.unitedpetclub.com/api/scheduling/v1/… with the access tokenYour software
6. RefreshPOST https://auth.unitedpetclub.com/connect/token with grant_type=refresh_tokenYour server, before the access token expires

A client that knows only the API’s address can find the rest. A call without a valid token answers 401 with a pointer to the API’s metadata:

GET /api/scheduling/v1/business HTTP/1.1
Host: api.unitedpetclub.com
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.unitedpetclub.com/.well-known/oauth-protected-resource"
Content-Type: application/problem+json

That document (RFC 9728) names the authorization server:

{
"resource": "https://api.unitedpetclub.com",
"authorization_servers": ["https://auth.unitedpetclub.com"],
"scopes_supported": ["scheduling:read", "appointment:read", "appointment:write", "client:read", "webhook:manage"],
"bearer_methods_supported": ["header"]
}

and the authorization server’s document (RFC 8414, https://auth.unitedpetclub.com/.well-known/oauth-authorization-server) names the authorization_endpoint, token_endpoint and registration_endpoint, the supported scopes, code_challenge_methods_supported (S256) and token_endpoint_auth_methods_supported (none for a public client). Both documents are anonymous and may be cached for an hour. The full examples are under Discovery metadata.

Register once per site or install, when your software is set up, and store what comes back. This example registers a WordPress site’s copy of a plugin as a public client, which has no secret:

POST /connect/register HTTP/1.1
Host: auth.unitedpetclub.com
Content-Type: application/json
{
"client_name": "Paws & Suds online booking",
"redirect_uris": ["https://pawsandsuds.example/wp-admin/admin.php?page=upc-booking-callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "scheduling:read appointment:read appointment:write",
"client_uri": "https://pawsandsuds.example",
"policy_uri": "https://pawsandsuds.example/privacy",
"contacts": ["web@pawsandsuds.example"],
"software_id": "upc-booking-for-wordpress",
"software_version": "1.4.2"
}
HTTP/1.1 201 Created
Content-Type: application/json
Cache-Control: no-store
{
"client_id": "upc-…",
"client_id_issued_at": 1790345678,
"redirect_uris": ["https://pawsandsuds.example/wp-admin/admin.php?page=upc-booking-callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"client_name": "Paws & Suds online booking",
"client_uri": "https://pawsandsuds.example",
"policy_uri": "https://pawsandsuds.example/privacy",
"scope": "scheduling:read appointment:read appointment:write",
"software_id": "upc-booking-for-wordpress",
"software_version": "1.4.2"
}
  • Public or confidential. With token_endpoint_auth_method none there is no secret. If your software runs on a server that can keep one (a WordPress site can, in its database), you may leave the field out (client_secret_basic) or send client_secret_post: the answer then also carries client_secret, shown this once, and client_secret_expires_at 0 (it does not expire). Either way, a self-registered app must use PKCE.
  • Redirect URIs match exactly, scheme, host, port, path and query included. Register every address the browser may come back to (up to 10), for example the exact loopback URL and port an agent listens on.
  • The name is what businesses see. It may not name United Pet Club; without one, businesses see the host of your first redirect URI.
  • Scopes. Ask only for what you need. Leave scope out to register all five Partner API scopes; no other scope can be registered.
  • Limits. At most 20 apps can be registered from one address in 24 hours, and self-registration pauses for the day when a platform-wide limit is reached: 429 with Retry-After: 3600. Sites on shared hosting share their server’s address, so never register per request, per business or per visitor.

Every refusal and field rule is listed under Self-registration.

For every connection attempt, make a PKCE pair and a state value, and keep both on your server with the user’s session:

// WordPress / PHP 7.1+
$verifier = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$challenge = rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '=');
$state = bin2hex(random_bytes(16));
// Node.js 18+
import crypto from "node:crypto";
const verifier = crypto.randomBytes(32).toString("base64url");
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
const state = crypto.randomBytes(16).toString("hex");

Then send the business’s browser to the authorize URL:

https://auth.unitedpetclub.com/connect/authorize
?response_type=code
&client_id=upc-…
&redirect_uri=https%3A%2F%2Fpawsandsuds.example%2Fwp-admin%2Fadmin.php%3Fpage%3Dupc-booking-callback
&scope=scheduling%3Aread%20appointment%3Aread%20appointment%3Awrite
&state=…
&code_challenge=…
&code_challenge_method=S256

A self-registered app must send code_challenge with code_challenge_method=S256. Without it, or with a redirect URI you did not register, the consent page explains the problem to the business and does not send the browser back.

The business signs in, acting as its business profile, and sees:

  • your app as an Unverified app, Registered by the host of your redirect URI (not by a publisher), with the advice to allow it only if it trusts that site;
  • the business it is connecting (it can switch to another of its businesses) and each permission you ask for;
  • After you choose, you’ll go back to your redirect URI’s host;
  • Add a payment card first when the business has no valid card on file. Allow stays off until it adds one (a link takes it to Finances → Billing) and comes back to the page.

On Allow the browser comes back with ?code=…&state=…; on Deny, with ?error=access_denied&…. Check state first. The code is valid for 10 minutes, once.

A public client sends its client_id and the code_verifier, and no client_secret:

Terminal window
curl -X POST https://auth.unitedpetclub.com/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=upcac_..." \
--data-urlencode "redirect_uri=https://pawsandsuds.example/wp-admin/admin.php?page=upc-booking-callback" \
-d "code_verifier=THE_VERIFIER_FROM_STEP_3" \
-d "client_id=upc-…"
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImRldi1hY2Nlc3MifQ...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "scheduling:read appointment:read appointment:write",
"refresh_token": "upcrt_..."
}

A confidential client adds its client_secret (or HTTP Basic). A public client that sends a secret is refused with 401 invalid_client. Store the refresh token with the business’s connection, encrypted: for a public client it is all anyone needs to get new access tokens.

Terminal window
curl https://api.unitedpetclub.com/api/scheduling/v1/services \
-H "Authorization: Bearer $ACCESS_TOKEN"

The token acts for the business that allowed your app and for nothing else: its services, open slots, estimates, bookings, appointments, clients and webhooks, within the scopes it granted. The booking guide covers every route.

You getWhyWhat to do
401 0010010000402The business disconnected your app, or United Pet Club suspended or revoked itStop calling; offer to connect again.
403 0010010000407The business has no valid payment card on fileTell the business to add one (Dashboard → Finances → Billing). Calls work again within about 10 seconds of the card being added.
503 0010010000503The connection or the card could not be checkedRetry after Retry-After seconds.
Terminal window
curl -X POST https://auth.unitedpetclub.com/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=upcrt_..." \
-d "client_id=upc-…"

Each refresh returns a new access token and a new refresh token; the one you used stops working. A refresh token lasts 90 days from when it was issued, so an integration that refreshes within that time never has to send the business through the consent page again. Refresh one business at a time from one worker (see Authentication → Refresh).

An AI agent can use this flow as it is. A client that follows RFC 9728 and RFC 8414 finds the authorization server from the API’s 401, registers itself, and asks a person at the business to approve it in a browser: an agent cannot connect itself to a business. An agent that only needs to read can use the United Pet Club MCP server instead (https://mcp.unitedpetclub.com, read-only, free, no card on file); an agent that books for a business calls this REST API with the business’s access token.

  • Register once and keep the client_id; do not register for every conversation.
  • An agent on a person’s computer can use a loopback redirect URI such as http://127.0.0.1:8765/callback. Register the exact address and port it listens on.
  • Ask for the fewest scopes that do the job. The business sees each one before it allows the agent.
  • No publisher. The app belongs to no United Pet Club profile, so nobody can manage it under Account → Developer → Applications: its secret cannot be rotated, and its redirect URIs and scopes cannot be changed. To change them, register a new client and have the businesses connect it.
  • Shown as unverified on the consent page and in the business’s Connected Apps, with the site it came from.
  • PKCE always, whether or not it has a secret.
  • The Partner API scopes only, for the businesses that allowed it. A business can disconnect it at any time, and United Pet Club can suspend or revoke it.

If you want businesses to see your company as the publisher, to rotate a secret and to see the businesses that connected your app, register it yourself under Account → Developer → Applications instead (see Registering an application). Those apps are confidential clients: your server keeps the secret.

Registering or operating an app means accepting the United Pet Club Platform Agreement for yourself and for the app. Section 13.7 (Apps, APIs & Integrations), new in version 1.2 of the Agreement, says in short:

  • The developer services are free unless United Pet Club tells you otherwise; rate limits and the permissions each business grants apply.
  • An app acts for a business only with its permission and only while the business keeps a valid payment card on file. The business is responsible for what the app does for it, including the bookings it makes and their fees.
  • United Pet Club may review and test any app at any time, with or without notice: test accounts, test requests, and test bookings, which carry no fees. You must not detect these reviews or treat them differently from ordinary use.
  • Self-registered apps are shown as unverified. United Pet Club may suspend, limit or revoke any app.

Bookings your app makes carry the business’s usual Commission and Platform Processing Fee (see Fees on bookings). The Agreement also requires the true price of every booking and sale (section 13.5) and sets a fine for trying to avoid fees (section 13.6).