# eBay REST Client Usage Guide This guide is the practical companion to the project README. Use it when you want to answer one of these questions quickly: - Which scopes do I need for a workflow? - Should I use `EbayClient` directly or `EbayOAuthClient` too? - Which wrapper method matches the eBay operation I want? - What are the implementation caveats around webhooks, disputes, uploads, or refunds? ## 1. Choose the Right Auth Model There are two supported patterns. ### Pattern A: Wrapper-first usage Use this when you want to call the REST wrappers and let the transport fetch tokens automatically. ```python from ebay_client import EbayClient from ebay_client.core.auth.models import EbayOAuthConfig oauth_config = EbayOAuthConfig( client_id="your-client-id", client_secret="your-client-secret", default_scopes=[ "https://api.ebay.com/oauth/api_scope/sell.inventory", ], ) client = EbayClient(oauth_config) items = client.inventory.get_inventory_items(limit=25) ``` This is the simplest mode. The transport asks the OAuth layer for a token whenever a wrapper call needs one. ### Pattern B: Shared OAuth and wrapper usage Use this when you need authorization-code flow, refresh tokens, or a shared persistent token store. ```python from ebay_client import EbayClient from ebay_client.core.auth.models import EbayOAuthConfig from ebay_client.core.auth.oauth import EbayOAuthClient from ebay_client.core.auth.store import InMemoryTokenStore store = InMemoryTokenStore() oauth_config = EbayOAuthConfig( client_id="your-client-id", client_secret="your-client-secret", redirect_uri="https://your-app.example/callback", default_scopes=[ "https://api.ebay.com/oauth/api_scope", "https://api.ebay.com/oauth/api_scope/commerce.notification.subscription", ], ) oauth = EbayOAuthClient(oauth_config, token_store=store) authorization_url = oauth.build_authorization_url(state="csrf-token") client = EbayClient(oauth_config, token_store=store) ``` Key point: if you want the wrapper layer and your own OAuth flow to see the same token state, use the same `TokenStore` instance for both. ## 2. Scope Planning The wrappers already encode the required scopes for each endpoint, but it still helps to know the scope families when you prepare credentials or interactive consent. ### Core scope families - Notification management: `https://api.ebay.com/oauth/api_scope` - Notification subscriptions: `https://api.ebay.com/oauth/api_scope/commerce.notification.subscription` - Notification subscription readonly: `https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly` - Inventory write: `https://api.ebay.com/oauth/api_scope/sell.inventory` - Inventory readonly: `https://api.ebay.com/oauth/api_scope/sell.inventory.readonly` - Fulfillment write: `https://api.ebay.com/oauth/api_scope/sell.fulfillment` - Fulfillment readonly: `https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly` - Account write: `https://api.ebay.com/oauth/api_scope/sell.account` - Account readonly: `https://api.ebay.com/oauth/api_scope/sell.account.readonly` - Refunds: `https://api.ebay.com/oauth/api_scope/sell.finances` - Payment disputes: `https://api.ebay.com/oauth/api_scope/sell.payment.dispute` - Media workflows: `https://api.ebay.com/oauth/api_scope/sell.inventory` - Feed endpoints accept one of several allowed scope families depending on the business domain behind the feed ### How the client behaves - Read wrappers often accept either the readonly scope or the broader write scope. - If a broader valid token is already cached, it is reused. - Write wrappers request the stronger scope directly. - If the current token does not satisfy the endpoint, the OAuth layer fetches a new token for the required scope set. - `default_scopes` are mainly the fallback used when you explicitly call OAuth methods without passing scopes yourself. ## 3. Top-Level Client Map `EbayClient` exposes these subclients: - `client.notification` - `client.inventory` - `client.fulfillment` - `client.account` - `client.feed` - `client.media` Use the generated model packages for request and response types: - `ebay_client.generated.notification.models` - `ebay_client.generated.inventory.models` - `ebay_client.generated.fulfillment.models` - `ebay_client.generated.account.models` - `ebay_client.generated.feed.models` - `ebay_client.generated.media.models` ## 4. Common Workflows This section maps typical seller workflows to wrapper methods. Quick source jumps: - facade: [ebay_client/client.py](../ebay_client/client.py) - notification wrapper: [ebay_client/notification/client.py](../ebay_client/notification/client.py) - inventory wrapper: [ebay_client/inventory/client.py](../ebay_client/inventory/client.py) - fulfillment wrapper: [ebay_client/fulfillment/client.py](../ebay_client/fulfillment/client.py) - account wrapper: [ebay_client/account/client.py](../ebay_client/account/client.py) - feed wrapper: [ebay_client/feed/client.py](../ebay_client/feed/client.py) - media wrapper: [ebay_client/media/client.py](../ebay_client/media/client.py) - FastAPI webhook example: [examples/fastapi_notification_webhook.py](../examples/fastapi_notification_webhook.py) - media workflows example: [examples/media_workflows.py](../examples/media_workflows.py) - inventory publish example: [examples/inventory_publish_flow.py](../examples/inventory_publish_flow.py) - feed task example: [examples/feed_task_flow.py](../examples/feed_task_flow.py) ### Notification: manage webhook subscriptions Typical flow: 1. inspect available topics with `get_topics()` 2. create or update a destination with `create_destination()` or `update_destination()` 3. create the subscription with `create_subscription()` 4. optionally attach filters with `create_subscription_filter()` 5. validate delivery with `test_subscription()` Use when you need: - destination CRUD - subscription CRUD - enable or disable subscriptions - lookup of eBay notification public keys ### Notification: receive and verify inbound events Inbound notifications do not use bearer-token auth. They use the `X-EBAY-SIGNATURE` header and eBay's public-key verification flow. Recommended server flow: 1. answer the GET challenge with `WebhookRequestHandler.handle_challenge()` 2. resolve the public key through `WebhookPublicKeyResolver` 3. verify the POST body with `WebhookSignatureValidator` 4. dispatch the parsed `WebhookEventEnvelope` Reference implementation: [examples/fastapi_notification_webhook.py](../examples/fastapi_notification_webhook.py). ### Inventory: read listings and inventory records Use these methods when you want to inspect the seller's inventory state: - `get_inventory_item()` - `get_inventory_items()` - `bulk_get_inventory_item()` - `get_offer()` - `get_offers()` - `get_listing_fees()` - `get_inventory_location()` - `get_inventory_locations()` This is the best starting point if your main use case is reading the data of already listed items. ### Inventory: create or update listings The common progression is: 1. create or replace the inventory item with `create_or_replace_inventory_item()` 2. configure location data if needed with `create_inventory_location()` or `update_inventory_location()` 3. create the offer with `create_offer()` 4. publish the offer with `publish_offer()` Bulk variants are available for larger batch flows: - `bulk_create_or_replace_inventory_item()` - `bulk_create_offer()` - `bulk_publish_offer()` - `bulk_update_price_quantity()` Caveat: several write methods require `content_language=...` and the wrapper enforces that explicitly. ### Inventory: multi-variation and mapping support Use these methods for additional listing structures: - inventory item groups: `get_inventory_item_group()`, `create_or_replace_inventory_item_group()`, `delete_inventory_item_group()` - product compatibility: `get_product_compatibility()`, `create_or_replace_product_compatibility()`, `delete_product_compatibility()` - location mapping: `get_sku_location_mapping()`, `create_or_replace_sku_location_mapping()`, `delete_sku_location_mapping()` - listing migration: `bulk_migrate_listing()` ### Fulfillment: orders and shipping fulfillments Typical order handling flow: 1. read orders with `get_orders()` or `get_order()` 2. create shipment details with `create_shipping_fulfillment()` 3. inspect resulting records with `get_shipping_fulfillments()` or `get_shipping_fulfillment()` Helper returned by create: - `CreatedShippingFulfillment`, which includes the `Location` header value and the extracted fulfillment ID ### Fulfillment: refunds Use `issue_refund()` with `IssueRefundRequest` when the seller workflow is eligible for the standard OAuth-backed refund path. Caveats: - this method uses the `sell.finances` scope instead of the normal fulfillment scope - eBay documents additional Digital Signatures requirements for some refund scenarios, especially for some EU and UK sellers - that Digital Signatures flow is not implemented in this client yet ### Fulfillment: payment disputes Dispute support is separated from regular fulfillment because eBay serves it from a different host and scope. Methods: - reads: `get_payment_dispute()`, `get_payment_dispute_summaries()`, `get_payment_dispute_activities()` - decisions: `contest_payment_dispute()`, `accept_payment_dispute()` - evidence files: `upload_evidence_file()`, `fetch_evidence_content()` - evidence metadata: `add_evidence()`, `update_evidence()` Caveats: - dispute methods use the `sell.payment.dispute` scope - dispute methods target `https://apiz.ebay.com/sell/fulfillment/v1/...` - the shared transport already supports absolute URLs, so this is handled inside the wrapper ### Account: business policies and seller capabilities Use the Account wrapper for policy management and seller-level configuration reads. Business policy coverage: - fulfillment policies: list, get, create, update, delete, get-by-name - payment policies: list, get, create, update, delete, get-by-name - return policies: list, get, create, update, delete, get-by-name Seller capability reads: - `get_privileges()` - `get_opted_in_programs()` This wrapper is a good fit when you need to prepare the policy IDs referenced by Inventory offers. ### Feed: task-based and scheduled bulk operations Use the Feed wrapper when the workflow is file-driven rather than single-resource CRUD. Task flow: 1. create a task with `create_task()` 2. upload its input file with `upload_file()` 3. inspect the task with `get_task()` 4. download results with `get_result_file()` Schedule flow: 1. inspect schedule templates with `get_schedule_templates()` or `get_schedule_template()` 2. create the recurring schedule with `create_schedule()` 3. inspect or update it with `get_schedule()` or `update_schedule()` 4. download the latest result file with `get_latest_result_file()` Helpers: - `CreatedFeedResource` - `FeedFileDownload` - `extract_feed_resource_id()` ### Media: image, document, and video workflows Use Media when you need hosted assets for listings. Images: - `create_image_from_file()` - `create_image_from_path()` - `create_image_from_url()` - `get_image()` Documents: - `create_document()` - `upload_document()` - `upload_document_from_path()` - `get_document()` - `wait_for_document()` - `create_upload_and_wait_document()` - `create_upload_and_wait_document_from_path()` - `create_document_from_url_and_wait()` Videos: - `create_video()` - `upload_video()` - `get_video()` - `wait_for_video()` - `create_upload_and_wait_video()` - `create_upload_and_wait_video_from_path()` Helpers: - `extract_resource_id()` - `guess_media_content_type()` - `VideoWorkflowResult` - `DocumentWorkflowResult` Reference implementation: [examples/media_workflows.py](../examples/media_workflows.py). ## 5. End-to-End Recipes These are the shortest practical paths through the client for the most common seller tasks. ### Recipe: read existing listed inventory data Use this when you mainly want to inspect what is already listed or staged in eBay. ```python from ebay_client import EbayClient from ebay_client.core.auth.models import EbayOAuthConfig client = EbayClient( EbayOAuthConfig( client_id="your-client-id", client_secret="your-client-secret", default_scopes=["https://api.ebay.com/oauth/api_scope/sell.inventory.readonly"], ) ) items = client.inventory.get_inventory_items(limit=50) offers = client.inventory.get_offers(limit=50) ``` Methods involved: - `get_inventory_items()` - `get_offers()` - optionally `get_inventory_item()` and `get_offer()` for detail reads ### Recipe: create an inventory item, create an offer, and publish it Use this when you want the standard listing flow through the Inventory API. ```python from ebay_client import EbayClient from ebay_client.core.auth.models import EbayOAuthConfig from ebay_client.generated.inventory.models import InventoryItem, OfferDetailsWithKeys client = EbayClient( EbayOAuthConfig( client_id="your-client-id", client_secret="your-client-secret", default_scopes=["https://api.ebay.com/oauth/api_scope/sell.inventory"], ) ) client.inventory.create_or_replace_inventory_item( "SKU-123", InventoryItem(), content_language="en-US", ) offer = client.inventory.create_offer(OfferDetailsWithKeys()) client.inventory.publish_offer(offer.offerId) ``` Operational notes: - you typically need business policy IDs and location data in place before the offer is valid - the wrapper requires `content_language` for the item write call because eBay expects it on several write endpoints - the exact request model fields depend on your marketplace and listing format ### Recipe: receive Notification webhook events in FastAPI Use the built-in webhook helpers instead of hand-parsing the signature format. Flow: 1. create a normal `EbayClient` 2. build `WebhookPublicKeyResolver` from `client.notification.get_public_key` 3. build `WebhookSignatureValidator` 4. use `WebhookRequestHandler` for both GET challenge and POST notification handling Working example: [examples/fastapi_notification_webhook.py](../examples/fastapi_notification_webhook.py). ### Recipe: upload a document or video asset for listings Use the Media wrapper helpers when you want the client to handle the create, upload, and polling loop for you. ```python from pathlib import Path from ebay_client import EbayClient from ebay_client.core.auth.models import EbayOAuthConfig from ebay_client.generated.media.models import CreateDocumentRequest client = EbayClient( EbayOAuthConfig( client_id="your-client-id", client_secret="your-client-secret", default_scopes=["https://api.ebay.com/oauth/api_scope/sell.inventory"], ) ) result = client.media.create_upload_and_wait_document_from_path( CreateDocumentRequest( documentType="USER_GUIDE_OR_MANUAL", languages=["en-US"], ), Path("./manual.pdf"), timeout_seconds=60.0, ) print(result.document_id) print(result.document.documentStatus) ``` Working example: [examples/media_workflows.py](../examples/media_workflows.py). ### Recipe: create a Feed task, upload its file, and download the result Use this when the operation is file-based and asynchronous. Flow: 1. create the task with `create_task()` 2. extract the returned `resource_id` 3. upload the task input file with `upload_file()` 4. poll `get_task()` until the task reaches the state you expect 5. download the result with `get_result_file()` This wrapper already provides typed helpers for the file downloads through `FeedFileDownload`. Working example: [examples/feed_task_flow.py](../examples/feed_task_flow.py). ### Recipe: dispute handling and evidence upload Use the Fulfillment dispute methods only when you have the dispute-specific scope. Flow: 1. read the dispute with `get_payment_dispute()` 2. upload the binary evidence file with `upload_evidence_file()` 3. attach or update the evidence metadata with `add_evidence()` or `update_evidence()` 4. contest or accept with `contest_payment_dispute()` or `accept_payment_dispute()` Caveat: these calls go through the alternate `apiz.ebay.com` fulfillment base URL inside the wrapper. ### Recipe: use the included examples directly The repository now includes these runnable example scripts: - [examples/inventory_publish_flow.py](../examples/inventory_publish_flow.py): create or replace an inventory item, create an offer, and publish it - [examples/feed_task_flow.py](../examples/feed_task_flow.py): create a feed task, upload an input file, poll its status, and optionally save the result file - [examples/media_workflows.py](../examples/media_workflows.py): image, document, and video asset workflows - [examples/fastapi_notification_webhook.py](../examples/fastapi_notification_webhook.py): FastAPI-based challenge and notification handling ## 6. Working With Generated Models The wrappers are intentionally thin. Request payloads and response types come from the generated Pydantic model packages. Example pattern: ```python from ebay_client import EbayClient from ebay_client.core.auth.models import EbayOAuthConfig from ebay_client.generated.media.models import CreateDocumentRequest client = EbayClient( EbayOAuthConfig( client_id="your-client-id", client_secret="your-client-secret", default_scopes=["https://api.ebay.com/oauth/api_scope/sell.inventory"], ) ) result = client.media.create_upload_and_wait_document_from_path( CreateDocumentRequest( documentType="USER_GUIDE_OR_MANUAL", languages=["en-US"], ), "./manual.pdf", ) ``` For any wrapper method that takes a payload, look up the corresponding generated request model in the matching `ebay_client.generated..models` package. ## 7. Operational Notes - The default token store is process-local and in-memory only. - Empty `202` and `204` responses are normalized by the shared transport so wrapper methods can return `None` cleanly for no-body success cases. - Multipart uploads are already handled by the transport and wrappers for Media, Feed, and dispute evidence uploads. - Absolute URL handling is already built into the transport so wrappers can call alternate eBay hosts when required. ## 8. Validation and Regeneration Run tests: ```powershell & .\.venv\Scripts\python.exe -m pytest ``` Regenerate model packages: ```powershell & .\.venv\Scripts\python.exe .\scripts\generate_clients.py ``` Generate just one API package: ```powershell & .\.venv\Scripts\python.exe .\scripts\generate_clients.py --api media ``` The generated models are written to `ebay_client/generated//models.py`.