{ "Protocol": "AIXE", "Version": "1.0", "ProtocolReference": { "ProtocolHome": "http://aixeprotocol.com/", "CanonicalUsageContract": "http://aixeprotocol.com/usage/?", "Whitepaper": "http://aixeprotocol.com/whitepaper/full-spec.html", "Inventor": "Gregory Oglethorpe" }, "Endpoint": "/aixe/businesses/search-businesses", "Method": "GET, POST", "SupportedMethods": [ "GET", "POST" ], "DiscoveryRequest": "GET /aixe/businesses/search-businesses/?", "ContentType": "application/json", "AccessType": "Public", "Purpose": "Find active IOD businesses near a ZIP code using up to 10 comma-separated keywords first, then a validated NAICS industry code only if the keyword search returns no matches.", "RequiredFields": { "ZipCode": { "Type": "string", "Required": true, "SubmittedIn": "GET query string or POST JSON body", "Description": "Exactly five ASCII digits identifying the user\u0027s search-center ZIP. Obtain it from the user; do not infer precise location from this request." } }, "OptionalFields": { "Keyword": { "Type": "string", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Conditionally required for the initial search. Supply 1 to 10 comma-separated single words, each at most 80 characters, with at most 1000 characters in the field. Spaces around commas are trimmed; empty entries and phrases are rejected. Include the user\u0027s word and relevant variants or synonyms: for \u0027photographer in 77568\u0027, use photographer,photography,photo,photos,photographic,portrait,portraits. Choose words from the user\u0027s request without assuming an unmentioned specialty. Do not include location words. Any supplied word may match; the server does not generate synonyms. Matching uses exact stored words extracted by splitting business names/descriptions and inventory names/short and long descriptions on whitespace, following database collation. Punctuation remains part of each word. Stop words are excluded; every other occurrence is retained, including repetitions within one item." }, "NAICSCode": { "Type": "string", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Conditionally required for the fallback search. Exactly six digits from a successful /aixe/naics/validate-naics-code lookup with IsValid true and a suitable title. Omit Keyword when using this field. The search also checks the code against the NAICS table." }, "RadiusMiles": { "Type": "number", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Search radius in miles, from 0.1 through 500; defaults to 20. Use the distance requested by the human." }, "MaximumResultCount": { "Type": "integer", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Business page size, from 1 through 100; defaults to 20." }, "MaximumProductCount": { "Type": "integer", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Matching active products per business, from 1 through 100; defaults to 100." }, "ProductOffset": { "Type": "integer", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Matching products to skip within each business, from 0 through 100000; defaults to 0. Increase by MaximumProductCount with other filters and business paging unchanged to retrieve remaining products." }, "Offset": { "Type": "integer", "Required": false, "SubmittedIn": "GET query string or POST JSON body", "Description": "Number of matches to skip, from 0 through 100000; defaults to 0. Use NextOffset with unchanged filters for the next page." } }, "ConditionalRequirements": "Supply exactly one nonblank Keyword or NAICSCode. Never both. ZipCode is always required.", "SearchWorkflow": [ "1. Choose up to 10 relevant words, including the user\u0027s term and reasonable variants or synonyms, and submit them as a comma-separated Keyword string. Example: photographer,photography,photo,photos,photographic,portrait,portraits. A single word remains valid. Search with Keyword, ZipCode, and RadiusMiles, starting at Offset 0.", "2. If SUCCESS returns any Results, show the relevant businesses. Do not switch to NAICS solely because there are fewer matches than the requested page size.", "3. If the initial keyword search returns SUCCESS with zero Results, recall the likely NAICS code from your training data/internal memory. For a coffee shop, a likely code is 722515. Read GET /aixe/naics/validate-naics-code/? and POST the candidate code there.", "4. Proceed only when the validator returns SUCCESS and IsValid true, and NAICSTitle fits the requested activity. Retry this search with NAICSCode instead of Keyword, using the same ZipCode and RadiusMiles and resetting Offset to 0.", "5. If you cannot recall a suitable code, or validation returns IsValid false or an unsuitable title, use /aixe/naics/search to find a candidate and validate it before the fallback search. Do not fabricate a code or treat a validation failure as zero business matches.", "6. If the validated NAICS fallback also returns SUCCESS with zero Results, explain that no matching businesses were found in the IOD directory within that radius and that there likely are none currently listed. This does not prove none exist outside IOD or that the directory is complete. Stop; do not repeatedly guess codes or enlarge the radius without the user\u0027s direction.", "7. Follow a selected result\u0027s ManifestEndpoint to discover what that business can do. Any subsequent business action requires its declared authorization." ], "RelatedEndpoints": { "ValidateNAICSCode": { "Endpoint": "/aixe/naics/validate-naics-code", "DiscoveryRequest": "GET /aixe/naics/validate-naics-code/?", "Method": "POST" }, "SearchNAICS": { "Endpoint": "/aixe/naics/search", "DiscoveryRequest": "GET /aixe/naics/search/?", "Method": "POST" } }, "BusinessRules": [ "Public read-only directory lookup; no login or BusinessKey required. Only active businesses are returned. GET accepts URL query parameters; POST accepts the same fields in a JSON body. Both use the same validation, results and paging. GET without parameters, with describe=true or aixe=true, or with an AIXE discovery Accept header returns the stored discovery contract.", "Each business includes Products containing its matching active inventory items, each listed once with ProductKey, ProductName, ProductPrice and KeywordMatchCount. Products are ordered by occurrence count descending, then name and public key. Business-only matches and NAICS searches have an empty Products list. MatchingProductCount gives the full matching item count; more remain when ProductOffset plus Products length is less than MatchingProductCount. Use public product keys to continue into the business\u0027s ordering endpoints after reading their contracts.", "Distances are approximate straight-line distances between ZIP reference coordinates, not business street addresses or driving distances. ZIP\u002B4 business addresses use their first five digits. Businesses without a usable matching ZIP location are excluded.", "NAICS fallback matches the business\u0027s exact industry code. Keyword and NAICS are alternative searches, not combined filters.", "Match any supplied keyword and count all matching stored occurrences, counting each stored occurrence once even if a search term is repeated. Keyword results are ordered by total matching word occurrence count descending, then nearest distance, business name and public key. Repeated words within one item count separately. NAICS results remain nearest first. All filtering, distance ordering, and paging are performed in SQL Server; only one bounded page is returned.", "SUCCESS with an empty Results array is a valid no-match result. An empty later page does not trigger fallback; fallback applies only to the initial search at Offset 0." ], "ExampleGetRequest": "GET /aixe/businesses/search-businesses?Keyword=photographer,photography\u0026ZipCode=77568\u0026RadiusMiles=20", "ExampleRequests": [ { "Keyword": "photographer,photography,photo,photos,photographic,portrait,portraits", "ZipCode": "77568", "RadiusMiles": 20 }, { "NAICSCode": "722515", "ZipCode": "77568", "RadiusMiles": 20 } ], "ResponseMeaning": { "SearchMode": "Keyword or NAICSCode.", "ZipCode": "Search-center ZIP.", "RadiusMiles": "Requested radius.", "Results": "BusinessKey, BusinessName, BusinessDescription, BusinessAddress (street address), BusinessAddress2 (suite or additional address line), BusinessCity, BusinessState, BusinessZip, BusinessCountry, BusinessWebsite, BusinessPhone, NAICSCode, NAICSTitle, ApproximateDistanceMiles, KeywordMatchCount, ManifestEndpoint, MatchingProductCount, and Products. Use the address fields together to show the business location. KeywordMatchCount is the number of matching word occurrences for keyword searches and zero for NAICS searches. Unavailable optional business details may be null. No owner, login, notification, or internal numeric ID fields are returned.", "HasMore": "Whether another page exists.", "NextOffset": "Starting offset for the next page or null.", "DistanceMeaning": "Explains the ZIP-based approximation." }, "Errors": [ { "SuccessCode": "VALIDATION_FAILED", "Meaning": "Missing/invalid ZIP, invalid code, more than 10 keywords, empty entries, phrases or overlong words, both/neither search filters, or invalid paging/radius.", "Recovery": "Correct the indicated inputs using this contract. Do not initiate the no-results fallback for an error response." }, { "SuccessCode": "FAILED", "Meaning": "Search did not complete.", "Recovery": "Do not interpret this as no matches." } ], "ActionResponse": { "RequiredResponseFields": [ "SuccessCode" ], "SuccessCodes": [ "SUCCESS" ], "FailureCodes": [ "VALIDATION_FAILED", "FAILED" ], "MissingOrEmptyResponse": "Treat as FAILED. Do not infer success from HTTP status." } }