For AI agents

How a script or an agent manages a paid member's products on PropertyManagementCo.co.uk. The same text as Markdown: https://www.propertymanagementco.co.uk/ai-agents.md

What this is

The member API lets a script or an AI agent manage a paid member's products and ranges on propertymanagementco.co.uk: create them, update them, add images from your own URLs, unpublish them, or mirror your whole catalogue in one call.

Paid members' products go live immediately; anything our checks flag waits for a human review and comes back as "moderation": "queued".

The API never deletes anything. DELETE unpublishes, and the member can restore from their profile.

Getting a key

Log in, open Profile → Accounts → API Keys, create a key and copy it: it is shown once. Keys are per directory and per member; one key covers all of that member's businesses here. Revoke a key at any time from the same page.

Base URL: https://www.propertymanagementco.co.uk/api/v1/ Header: X-API-KEY: propertymanagementco_live_7f3a9c21… Keys look like propertymanagementco_live_ followed by 64 hex characters.

curl -s -X GET 'https://www.propertymanagementco.co.uk/api/v1/me' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…'

Which business

Every write names a business with business_id. If you own one business on this directory you may leave it out. GET /api/v1/me lists your businesses with their ids, your tier, cap and usage.

Endpoints

MethodPathDoes
GET/api/v1/meyou, your businesses, tier, cap, usage, rate-limit state
GET/api/v1/categoriesthis directory's categories: the only valid category values
GET/api/v1/productslist (business_id, page, status=live|unpublished); 50 per page
POST/api/v1/productscreate one
GET/api/v1/products/{id}read one
PATCH/api/v1/products/{id}update the fields sent; null clears a nullable field
DELETE/api/v1/products/{id}unpublish (204)
POST/api/v1/products/{id}/images{"urls":[…]} replaces the images (max 8)
DELETE/api/v1/products/{id}/imagesremove all images (204)
GET/api/v1/rangeslist ranges
POST/api/v1/rangescreate a range (heading; sku, description, link, hero_url optional)
GET/api/v1/ranges/{id}read a range
PATCH/api/v1/ranges/{id}update a range
DELETE/api/v1/ranges/{id}unpublish a range (204)
POST/api/v1/products/syncthe whole catalogue of one business in one call

Product fields

FieldRule
headingrequired on create; 3–150 characters
descriptionoptional; HTML or text up to 20,000 characters; unsafe markup is removed
priceoptional free text up to 150 characters, e.g. "£49.99 per m²"
linkoptional http(s) URL up to 500 characters (shown to visitors on paid listings)
categoryoptional; a slug from GET /api/v1/categories
skuyour own code; optional on POST, required in sync; letters, digits . _ - / up to 64; unique per business, matched regardless of case
range_id or range_skuoptional; a range of the same business
image_urlsoptional; up to 8 http(s) URLs we fetch (jpeg/png/webp/gif, 5 MB each, public hosts only)
curl -s -X POST 'https://www.propertymanagementco.co.uk/api/v1/products' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"sku":"OAK-190","heading":"Natural Oak 190mm Plank","price":"£49.99 per m²","category":"engineered-wood-flooring","image_urls":["https://shop.example.com/i/oak.jpg"]}'
{"product":{"id":1234,"sku":"OAK-190","heading":"Natural Oak 190mm Plank","status":"live","url":"https://www.propertymanagementco.co.uk/product/natural-oak-190mm-plank-1234","...":"..."},"moderation":"published","rejected_images":[]}

Sync: your whole catalogue in one call

POST /api/v1/products/sync with business_id and products (up to 500 items, each with a sku). Items are matched on sku (regardless of case): known → updated, new → created, and any product of yours with a sku that is missing from the payload is unpublished. Products without a sku (made in the profile form) are never touched; the response counts them as unmanaged.

Updates are partial, like PATCH: a field an item leaves out keeps its current value, so you can sync just sku + price without touching descriptions or images. Send a field with null to clear it.

Moderation inside a run: the first 50 new or changed items are checked by the AI checker; beyond that the deterministic checks alone decide and the queue row says so. An item a moderator has suspended stays queued however many times you send it, until a moderator approves it.

Safety valve: if a run would unpublish more than half of your live catalogue (and at least two products) it is refused with 409 removals_exceed_threshold. Send "confirm_removals": true to go ahead. A feed whose every item is rejected removes nothing.

Images: at most 100 image fetches per run. Items still waiting are listed in images_pending; run sync again (unchanged items cost nothing) or call POST /api/v1/products/{id}/images.

One sync per business every 10 minutes (429 sync_too_soon with Retry-After).

curl -s -X POST 'https://www.propertymanagementco.co.uk/api/v1/products/sync' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"confirm_removals":false,"products":[{"sku":"OAK-190","heading":"Natural Oak 190mm Plank","price":"£49.99 per m²"},{"sku":"WAL-150","heading":"Walnut 150mm Plank"}]}'
{"created":1,"updated":1,"unpublished":0,"unchanged":0,"unmanaged":2,"rejected":[],"queued":[],"images_pending":[],"products":[{"sku":"OAK-190","id":1234,"action":"updated","moderation":"unchanged"},{"sku":"WAL-150","id":1301,"action":"created","moderation":"published"}]}

Moderation, caps and limits

Clean products from paid members publish at once ("moderation": "published"). Anything our automated checks flag waits for review ("queued") and appears once approved.

Caps: 500 products per business. Creates beyond the cap are rejected per item with over_cap; updates always work.

Rate limit: 600 requests per hour per key. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; over the limit you get 429 with Retry-After. Request bodies are limited to 2 MB.

Errors

Errors are {"error":{"code":"…","message":"…","field":"…","details":{…}}}; field and details appear when they apply. 422 validation_failed lists every failing field in details so you can fix a payload in one go.

HTTPCodes
400bad_json
401missing_key, invalid_key, key_revoked
403api_disabled, paid_tier_required, business_not_live
404unknown_domain, business_not_found, product_not_found, range_not_found, not_found
409duplicate_sku, removals_exceed_threshold
413payload_too_large
422validation_failed, invalid_category, invalid_range, sku_required, duplicate_sku_in_payload, over_cap, image_url_invalid, image_url_private, image_unsupported, image_too_large, image_fetch_failed
429rate_limited, sync_too_soon
500server_error

Checklist

A quick run-through you can paste into a terminal (replace the key and ids).

curl -s -X GET 'https://www.propertymanagementco.co.uk/api/v1/me' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…'
curl -s -X GET 'https://www.propertymanagementco.co.uk/api/v1/categories' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…'
curl -s -X POST 'https://www.propertymanagementco.co.uk/api/v1/products' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"sku":"TEST-1","heading":"Test product","category":"not-a-category"}'   # 422 invalid_category
curl -s -X POST 'https://www.propertymanagementco.co.uk/api/v1/products' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"sku":"TEST-1","heading":"Test product","image_urls":["https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"]}'
curl -s -X POST 'https://www.propertymanagementco.co.uk/api/v1/products/sync' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"products":[{"sku":"TEST-1","heading":"Test product"}]}'
curl -s -X DELETE 'https://www.propertymanagementco.co.uk/api/v1/products/1234?business_id=42' -H 'X-API-KEY: propertymanagementco_live_7f3a9c21…'   # 204