@authorizedretailers/spec 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/LICENSE-SPEC +396 -0
- package/README.md +130 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.js +7 -0
- package/dist/identity.d.ts +27 -0
- package/dist/identity.js +92 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/jcs.d.ts +5 -0
- package/dist/jcs.js +47 -0
- package/dist/jws.d.ts +42 -0
- package/dist/jws.js +90 -0
- package/dist/published.d.ts +15 -0
- package/dist/published.js +15 -0
- package/dist/schemas.d.ts +519 -0
- package/dist/schemas.js +662 -0
- package/dist/timestamp.d.ts +6 -0
- package/dist/timestamp.js +26 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.js +2 -0
- package/dist/validate.d.ts +28 -0
- package/dist/validate.js +147 -0
- package/fixtures/file/invalid/full-all-mixed.json +47 -0
- package/fixtures/file/invalid/full-amazon-missing-seller-id.json +45 -0
- package/fixtures/file/invalid/full-attached-jws.json +50 -0
- package/fixtures/file/invalid/full-authorization-expires-after-file.json +46 -0
- package/fixtures/file/invalid/full-duplicate-authorization-id.json +80 -0
- package/fixtures/file/invalid/full-empty-territories.json +43 -0
- package/fixtures/file/invalid/full-expires-before-issued.json +46 -0
- package/fixtures/file/invalid/full-expiry-over-180-days.json +46 -0
- package/fixtures/file/invalid/full-extra-property.json +47 -0
- package/fixtures/file/invalid/full-impossible-date.json +46 -0
- package/fixtures/file/invalid/full-lowercase-territory.json +45 -0
- package/fixtures/file/invalid/full-missing-authorization-expires.json +45 -0
- package/fixtures/file/invalid/full-missing-file-expires.json +45 -0
- package/fixtures/file/invalid/full-missing-product-lines.json +43 -0
- package/fixtures/file/invalid/full-missing-scope.json +37 -0
- package/fixtures/file/invalid/full-missing-territories.json +42 -0
- package/fixtures/file/invalid/full-name-only-retailer.json +30 -0
- package/fixtures/file/invalid/full-no-channels.json +31 -0
- package/fixtures/file/invalid/full-non-utc-timestamp.json +46 -0
- package/fixtures/file/invalid/full-physical-missing-country.json +50 -0
- package/fixtures/file/invalid/full-signature-missing-kid.json +49 -0
- package/fixtures/file/invalid/full-unknown-channel-type.json +50 -0
- package/fixtures/file/invalid/full-web-domain-is-url.json +46 -0
- package/fixtures/file/invalid/full-web-missing-domain.json +45 -0
- package/fixtures/file/invalid/full-wildcard-mixed.json +46 -0
- package/fixtures/file/invalid/full-wrong-spec.json +46 -0
- package/fixtures/file/invalid/pointer-http-url.json +9 -0
- package/fixtures/file/invalid/private-missing-verify.json +8 -0
- package/fixtures/file/invalid/private-with-authorizations.json +45 -0
- package/fixtures/file/invalid/unknown-form.json +9 -0
- package/fixtures/file/valid/full-fractional-seconds.json +46 -0
- package/fixtures/file/valid/full-max-expiry.json +46 -0
- package/fixtures/file/valid/full-no-authorizations.json +11 -0
- package/fixtures/file/valid/full-signed.json +50 -0
- package/fixtures/file/valid/full-unsigned-handwritten.json +46 -0
- package/fixtures/file/valid/full-worldwide-physical-proposed.json +82 -0
- package/fixtures/file/valid/pointer.json +9 -0
- package/fixtures/file/valid/private.json +9 -0
- package/fixtures/manifest.json +434 -0
- package/fixtures/verify-request/invalid/domain-with-scheme.json +10 -0
- package/fixtures/verify-request/invalid/missing-channel.json +5 -0
- package/fixtures/verify-request/invalid/physical-channel.json +10 -0
- package/fixtures/verify-request/invalid/retailer-name-only.json +6 -0
- package/fixtures/verify-request/invalid/wildcard-territory.json +10 -0
- package/fixtures/verify-request/valid/amazon.json +10 -0
- package/fixtures/verify-request/valid/web-no-product-line.json +8 -0
- package/fixtures/verify-response/invalid/authorized-missing-authorization-id.json +12 -0
- package/fixtures/verify-response/invalid/missing-observed.json +12 -0
- package/fixtures/verify-response/invalid/missing-signature.json +9 -0
- package/fixtures/verify-response/invalid/missing-valid-until.json +12 -0
- package/fixtures/verify-response/invalid/observed-without-last-seen.json +13 -0
- package/fixtures/verify-response/invalid/unknown-status.json +13 -0
- package/fixtures/verify-response/invalid/unlisted-observed.json +13 -0
- package/fixtures/verify-response/invalid/unlisted-with-authorization-id.json +12 -0
- package/fixtures/verify-response/invalid/unlisted-with-reason.json +12 -0
- package/fixtures/verify-response/invalid/valid-until-before-checked.json +13 -0
- package/fixtures/verify-response/invalid/valid-until-over-24h.json +13 -0
- package/fixtures/verify-response/invalid/with-tier.json +14 -0
- package/fixtures/verify-response/valid/authorized-observed.json +15 -0
- package/fixtures/verify-response/valid/authorized.json +13 -0
- package/fixtures/verify-response/valid/brand-unverified-no-reason.json +11 -0
- package/fixtures/verify-response/valid/brand-unverified-reason.json +12 -0
- package/fixtures/verify-response/valid/disputed.json +13 -0
- package/fixtures/verify-response/valid/expired.json +13 -0
- package/fixtures/verify-response/valid/unlisted.json +11 -0
- package/fixtures/verify-response/valid/with-signal.json +19 -0
- package/package.json +76 -0
- package/schemas/common.json +170 -0
- package/schemas/file-full.json +23 -0
- package/schemas/file-pointer.json +14 -0
- package/schemas/file-private.json +14 -0
- package/schemas/file.json +13 -0
- package/schemas/verify-request.json +18 -0
- package/schemas/verify-response.json +61 -0
- package/spec/spec-v0.1.md +282 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export declare const SPEC_VERSION: "authorized-retailers/0.1";
|
|
2
|
+
export type SpecVersion = typeof SPEC_VERSION;
|
|
3
|
+
/** RFC 3339 UTC timestamp, e.g. "2026-09-23T00:00:00Z". */
|
|
4
|
+
export type Timestamp = string;
|
|
5
|
+
export interface Brand {
|
|
6
|
+
name: string;
|
|
7
|
+
domain: string;
|
|
8
|
+
}
|
|
9
|
+
export type MarketplaceChannelType = "amazon" | "walmart" | "ebay";
|
|
10
|
+
export interface MarketplaceChannel {
|
|
11
|
+
type: MarketplaceChannelType;
|
|
12
|
+
marketplace: string;
|
|
13
|
+
seller_id: string;
|
|
14
|
+
}
|
|
15
|
+
export interface WebChannel {
|
|
16
|
+
type: "web";
|
|
17
|
+
domain: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PhysicalChannel {
|
|
20
|
+
type: "physical";
|
|
21
|
+
address: string;
|
|
22
|
+
country: string;
|
|
23
|
+
}
|
|
24
|
+
/** A channel an agent can see and match on (section 6). */
|
|
25
|
+
export type VerifiableChannel = MarketplaceChannel | WebChannel;
|
|
26
|
+
export type Channel = VerifiableChannel | PhysicalChannel;
|
|
27
|
+
export type ChannelType = Channel["type"];
|
|
28
|
+
export interface Party {
|
|
29
|
+
name: string;
|
|
30
|
+
entity_id?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Scope {
|
|
33
|
+
/** ISO 3166-1 alpha-2 codes, or exactly ["*"]. */
|
|
34
|
+
territories: string[];
|
|
35
|
+
/** Named product lines, or exactly ["all"]. */
|
|
36
|
+
product_lines: string[];
|
|
37
|
+
}
|
|
38
|
+
export interface Authorization {
|
|
39
|
+
id: string;
|
|
40
|
+
retailer: Party;
|
|
41
|
+
channels: Channel[];
|
|
42
|
+
scope: Scope;
|
|
43
|
+
proposed_by?: Party | null;
|
|
44
|
+
expires: Timestamp;
|
|
45
|
+
}
|
|
46
|
+
export interface Signature {
|
|
47
|
+
kid: string;
|
|
48
|
+
/** Detached compact JWS: "<header>..<signature>". */
|
|
49
|
+
jws: string;
|
|
50
|
+
}
|
|
51
|
+
export interface FullFile {
|
|
52
|
+
spec: SpecVersion;
|
|
53
|
+
form: "full";
|
|
54
|
+
brand: Brand;
|
|
55
|
+
issued: Timestamp;
|
|
56
|
+
expires: Timestamp;
|
|
57
|
+
authorizations: Authorization[];
|
|
58
|
+
signature?: Signature;
|
|
59
|
+
}
|
|
60
|
+
export interface PointerFile {
|
|
61
|
+
spec: SpecVersion;
|
|
62
|
+
form: "pointer";
|
|
63
|
+
brand: Brand;
|
|
64
|
+
list: string;
|
|
65
|
+
}
|
|
66
|
+
export interface PrivateFile {
|
|
67
|
+
spec: SpecVersion;
|
|
68
|
+
form: "private";
|
|
69
|
+
brand: Brand;
|
|
70
|
+
verify: string;
|
|
71
|
+
}
|
|
72
|
+
export type AuthorizedRetailersFile = FullFile | PointerFile | PrivateFile;
|
|
73
|
+
export type FileForm = AuthorizedRetailersFile["form"];
|
|
74
|
+
export interface VerifyRequest {
|
|
75
|
+
brand_domain: string;
|
|
76
|
+
channel: VerifiableChannel;
|
|
77
|
+
territory: string;
|
|
78
|
+
/** Defaults to "all". */
|
|
79
|
+
product_line?: string;
|
|
80
|
+
}
|
|
81
|
+
export type VerifyStatus = "authorized" | "unlisted" | "expired" | "brand_unverified" | "disputed";
|
|
82
|
+
export type UnverifiedReason = "not_registered" | "domain_unlinked" | "verification_lapsed";
|
|
83
|
+
export interface Signal {
|
|
84
|
+
type: string;
|
|
85
|
+
observed: Timestamp;
|
|
86
|
+
detail?: string;
|
|
87
|
+
}
|
|
88
|
+
/** Section 7: the registry saw this seller selling on the channel within the last 30 days. */
|
|
89
|
+
export interface Observation {
|
|
90
|
+
last_seen: Timestamp;
|
|
91
|
+
}
|
|
92
|
+
interface VerifyResponseBase {
|
|
93
|
+
checked: Timestamp;
|
|
94
|
+
/** At most 24 hours after `checked` (section 9). */
|
|
95
|
+
valid_until: Timestamp;
|
|
96
|
+
/** Non-null only on `authorized` answers (section 7). */
|
|
97
|
+
observed: Observation | null;
|
|
98
|
+
signals: Signal[];
|
|
99
|
+
signature: Signature;
|
|
100
|
+
}
|
|
101
|
+
export interface VerifyResponseWithAuthorization extends VerifyResponseBase {
|
|
102
|
+
status: "authorized" | "expired" | "disputed";
|
|
103
|
+
authorization_id: string;
|
|
104
|
+
expires: Timestamp;
|
|
105
|
+
}
|
|
106
|
+
export interface VerifyResponseUnlisted extends VerifyResponseBase {
|
|
107
|
+
status: "unlisted";
|
|
108
|
+
}
|
|
109
|
+
export interface VerifyResponseBrandUnverified extends VerifyResponseBase {
|
|
110
|
+
status: "brand_unverified";
|
|
111
|
+
reason?: UnverifiedReason;
|
|
112
|
+
}
|
|
113
|
+
export type VerifyResponse = VerifyResponseWithAuthorization | VerifyResponseUnlisted | VerifyResponseBrandUnverified;
|
|
114
|
+
/** A verify response before the registry attaches its signature. */
|
|
115
|
+
export type UnsignedVerifyResponse = VerifyResponse extends infer R ? R extends VerifyResponseBase ? Omit<R, "signature"> : never : never;
|
|
116
|
+
export {};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AuthorizedRetailersFile, Channel, Scope, FullFile, PointerFile, PrivateFile, VerifyRequest, VerifyResponse } from "./types.js";
|
|
2
|
+
export type ErrorCode = "schema" | "invalid_timestamp" | "expires_not_after_issued" | "file_expiry_exceeds_max" | "authorization_expires_after_file" | "duplicate_authorization_id" | "valid_until_not_after_checked" | "valid_until_exceeds_max";
|
|
3
|
+
export interface ValidationError {
|
|
4
|
+
code: ErrorCode;
|
|
5
|
+
/** JSON Pointer into the instance, e.g. "/authorizations/0/expires". */
|
|
6
|
+
path: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
export type ValidationResult<T> = {
|
|
10
|
+
valid: true;
|
|
11
|
+
value: T;
|
|
12
|
+
errors: [];
|
|
13
|
+
} | {
|
|
14
|
+
valid: false;
|
|
15
|
+
errors: ValidationError[];
|
|
16
|
+
};
|
|
17
|
+
/** Validates an authorized-retailers.json file of any form. */
|
|
18
|
+
export declare function validateFile(input: unknown): ValidationResult<AuthorizedRetailersFile>;
|
|
19
|
+
export declare function validateFullFile(input: unknown): ValidationResult<FullFile>;
|
|
20
|
+
export declare function validatePointerFile(input: unknown): ValidationResult<PointerFile>;
|
|
21
|
+
export declare function validatePrivateFile(input: unknown): ValidationResult<PrivateFile>;
|
|
22
|
+
/** Validates a canonical verify request. Call normalizeVerifyRequest on raw agent input first. */
|
|
23
|
+
export declare function validateVerifyRequest(input: unknown): ValidationResult<VerifyRequest>;
|
|
24
|
+
export declare function validateVerifyResponse(input: unknown): ValidationResult<VerifyResponse>;
|
|
25
|
+
/** Validates one authorization scope (§3), e.g. before a registry stores it. */
|
|
26
|
+
export declare function validateScope(input: unknown): ValidationResult<Scope>;
|
|
27
|
+
/** Validates one channel identifier (§6), e.g. before a registry stores it. */
|
|
28
|
+
export declare function validateChannel(input: unknown): ValidationResult<Channel>;
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Validator } from "@cfworker/json-schema";
|
|
2
|
+
import { common, file, fileFull, filePointer, filePrivate, verifyRequest, verifyResponse } from "./schemas.js";
|
|
3
|
+
import { MAX_ANSWER_VALIDITY_MS, MAX_EXPIRY_MS } from "./constants.js";
|
|
4
|
+
import { parseTimestamp } from "./timestamp.js";
|
|
5
|
+
const ALL_SCHEMAS = [common, fileFull, filePointer, filePrivate, file, verifyRequest, verifyResponse];
|
|
6
|
+
const scopeSchema = { $id: "https://authorizedretailers.ai/schemas/0.1/scope.json", $ref: "common.json#/$defs/scope" };
|
|
7
|
+
const channelSchema = { $id: "https://authorizedretailers.ai/schemas/0.1/channel.json", $ref: "common.json#/$defs/channel" };
|
|
8
|
+
function makeValidator(root) {
|
|
9
|
+
const v = new Validator(root, "2020-12", false);
|
|
10
|
+
for (const s of ALL_SCHEMAS)
|
|
11
|
+
if (s !== root)
|
|
12
|
+
v.addSchema(s);
|
|
13
|
+
return v;
|
|
14
|
+
}
|
|
15
|
+
// Built lazily: compiling at module load would slow Worker cold starts for callers that never validate.
|
|
16
|
+
const validators = new Map();
|
|
17
|
+
function schemaErrors(root, instance) {
|
|
18
|
+
let v = validators.get(root);
|
|
19
|
+
if (!v) {
|
|
20
|
+
v = makeValidator(root);
|
|
21
|
+
validators.set(root, v);
|
|
22
|
+
}
|
|
23
|
+
const result = v.validate(instance);
|
|
24
|
+
if (result.valid)
|
|
25
|
+
return [];
|
|
26
|
+
return dedupe(result.errors.map(toError));
|
|
27
|
+
}
|
|
28
|
+
function toError(unit) {
|
|
29
|
+
const path = unit.instanceLocation.replace(/^#/, "");
|
|
30
|
+
return { code: "schema", path, message: `${unit.error} (${unit.keywordLocation})` };
|
|
31
|
+
}
|
|
32
|
+
function dedupe(errors) {
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
return errors.filter((e) => {
|
|
35
|
+
const k = `${e.path}|${e.message}`;
|
|
36
|
+
if (seen.has(k))
|
|
37
|
+
return false;
|
|
38
|
+
seen.add(k);
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function done(errors, value) {
|
|
43
|
+
return errors.length === 0 ? { valid: true, value: value, errors: [] } : { valid: false, errors };
|
|
44
|
+
}
|
|
45
|
+
function ts(errors, path, value) {
|
|
46
|
+
const ms = parseTimestamp(value);
|
|
47
|
+
if (ms === null)
|
|
48
|
+
errors.push({ code: "invalid_timestamp", path, message: `not a valid UTC timestamp: ${value}` });
|
|
49
|
+
return ms;
|
|
50
|
+
}
|
|
51
|
+
/** Rules the JSON Schema cannot express (sections 5 and 10). Assumes the file already passed the schema. */
|
|
52
|
+
function fullFileRules(f) {
|
|
53
|
+
const errors = [];
|
|
54
|
+
const issued = ts(errors, "/issued", f.issued);
|
|
55
|
+
const expires = ts(errors, "/expires", f.expires);
|
|
56
|
+
if (issued !== null && expires !== null) {
|
|
57
|
+
if (expires <= issued) {
|
|
58
|
+
errors.push({ code: "expires_not_after_issued", path: "/expires", message: "file expires must be after issued" });
|
|
59
|
+
}
|
|
60
|
+
else if (expires - issued > MAX_EXPIRY_MS) {
|
|
61
|
+
errors.push({
|
|
62
|
+
code: "file_expiry_exceeds_max",
|
|
63
|
+
path: "/expires",
|
|
64
|
+
message: "file expires more than 180 days after issued (section 10)",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const ids = new Set();
|
|
69
|
+
f.authorizations.forEach((a, i) => {
|
|
70
|
+
const base = `/authorizations/${i}`;
|
|
71
|
+
if (ids.has(a.id)) {
|
|
72
|
+
errors.push({ code: "duplicate_authorization_id", path: `${base}/id`, message: `duplicate authorization id ${a.id}` });
|
|
73
|
+
}
|
|
74
|
+
ids.add(a.id);
|
|
75
|
+
const aExpires = ts(errors, `${base}/expires`, a.expires);
|
|
76
|
+
if (aExpires !== null && expires !== null && aExpires > expires) {
|
|
77
|
+
errors.push({
|
|
78
|
+
code: "authorization_expires_after_file",
|
|
79
|
+
path: `${base}/expires`,
|
|
80
|
+
message: "an authorization's expires MUST NOT be later than the file's expires (section 5)",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return errors;
|
|
85
|
+
}
|
|
86
|
+
function verifyResponseRules(r) {
|
|
87
|
+
const errors = [];
|
|
88
|
+
const checked = ts(errors, "/checked", r.checked);
|
|
89
|
+
const validUntil = ts(errors, "/valid_until", r.valid_until);
|
|
90
|
+
if ("expires" in r)
|
|
91
|
+
ts(errors, "/expires", r.expires);
|
|
92
|
+
for (const [i, s] of r.signals.entries())
|
|
93
|
+
ts(errors, `/signals/${i}/observed`, s.observed);
|
|
94
|
+
if (r.observed)
|
|
95
|
+
ts(errors, "/observed/last_seen", r.observed.last_seen);
|
|
96
|
+
if (checked !== null && validUntil !== null) {
|
|
97
|
+
if (validUntil <= checked) {
|
|
98
|
+
errors.push({ code: "valid_until_not_after_checked", path: "/valid_until", message: "valid_until must be after checked" });
|
|
99
|
+
}
|
|
100
|
+
else if (validUntil - checked > MAX_ANSWER_VALIDITY_MS) {
|
|
101
|
+
errors.push({
|
|
102
|
+
code: "valid_until_exceeds_max",
|
|
103
|
+
path: "/valid_until",
|
|
104
|
+
message: "valid_until MUST be no later than 24 hours after checked (section 9)",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return errors;
|
|
109
|
+
}
|
|
110
|
+
/** Validates an authorized-retailers.json file of any form. */
|
|
111
|
+
export function validateFile(input) {
|
|
112
|
+
const errors = schemaErrors(file, input);
|
|
113
|
+
if (errors.length === 0 && input.form === "full") {
|
|
114
|
+
errors.push(...fullFileRules(input));
|
|
115
|
+
}
|
|
116
|
+
return done(errors, input);
|
|
117
|
+
}
|
|
118
|
+
export function validateFullFile(input) {
|
|
119
|
+
const errors = schemaErrors(fileFull, input);
|
|
120
|
+
if (errors.length === 0)
|
|
121
|
+
errors.push(...fullFileRules(input));
|
|
122
|
+
return done(errors, input);
|
|
123
|
+
}
|
|
124
|
+
export function validatePointerFile(input) {
|
|
125
|
+
return done(schemaErrors(filePointer, input), input);
|
|
126
|
+
}
|
|
127
|
+
export function validatePrivateFile(input) {
|
|
128
|
+
return done(schemaErrors(filePrivate, input), input);
|
|
129
|
+
}
|
|
130
|
+
/** Validates a canonical verify request. Call normalizeVerifyRequest on raw agent input first. */
|
|
131
|
+
export function validateVerifyRequest(input) {
|
|
132
|
+
return done(schemaErrors(verifyRequest, input), input);
|
|
133
|
+
}
|
|
134
|
+
export function validateVerifyResponse(input) {
|
|
135
|
+
const errors = schemaErrors(verifyResponse, input);
|
|
136
|
+
if (errors.length === 0)
|
|
137
|
+
errors.push(...verifyResponseRules(input));
|
|
138
|
+
return done(errors, input);
|
|
139
|
+
}
|
|
140
|
+
/** Validates one authorization scope (§3), e.g. before a registry stores it. */
|
|
141
|
+
export function validateScope(input) {
|
|
142
|
+
return done(schemaErrors(scopeSchema, input), input);
|
|
143
|
+
}
|
|
144
|
+
/** Validates one channel identifier (§6), e.g. before a registry stores it. */
|
|
145
|
+
export function validateChannel(input) {
|
|
146
|
+
return done(schemaErrors(channelSchema, input), input);
|
|
147
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-12-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US",
|
|
21
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "walmart",
|
|
25
|
+
"marketplace": "US",
|
|
26
|
+
"seller_id": "101234567"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "web",
|
|
30
|
+
"domain": "retailer.example"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"scope": {
|
|
34
|
+
"territories": [
|
|
35
|
+
"US",
|
|
36
|
+
"CA"
|
|
37
|
+
],
|
|
38
|
+
"product_lines": [
|
|
39
|
+
"all",
|
|
40
|
+
"running"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"proposed_by": null,
|
|
44
|
+
"expires": "2026-12-22T00:00:00Z"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-12-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"type": "walmart",
|
|
24
|
+
"marketplace": "US",
|
|
25
|
+
"seller_id": "101234567"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "web",
|
|
29
|
+
"domain": "retailer.example"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"scope": {
|
|
33
|
+
"territories": [
|
|
34
|
+
"US",
|
|
35
|
+
"CA"
|
|
36
|
+
],
|
|
37
|
+
"product_lines": [
|
|
38
|
+
"all"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"proposed_by": null,
|
|
42
|
+
"expires": "2026-12-22T00:00:00Z"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-12-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US",
|
|
21
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "walmart",
|
|
25
|
+
"marketplace": "US",
|
|
26
|
+
"seller_id": "101234567"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "web",
|
|
30
|
+
"domain": "retailer.example"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"scope": {
|
|
34
|
+
"territories": [
|
|
35
|
+
"US",
|
|
36
|
+
"CA"
|
|
37
|
+
],
|
|
38
|
+
"product_lines": [
|
|
39
|
+
"all"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"proposed_by": null,
|
|
43
|
+
"expires": "2026-12-22T00:00:00Z"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"signature": {
|
|
47
|
+
"kid": "ar-2026-09",
|
|
48
|
+
"jws": "aGVhZA.cGF5bG9hZA.c2ln"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-12-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US",
|
|
21
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "walmart",
|
|
25
|
+
"marketplace": "US",
|
|
26
|
+
"seller_id": "101234567"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "web",
|
|
30
|
+
"domain": "retailer.example"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"scope": {
|
|
34
|
+
"territories": [
|
|
35
|
+
"US",
|
|
36
|
+
"CA"
|
|
37
|
+
],
|
|
38
|
+
"product_lines": [
|
|
39
|
+
"all"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"proposed_by": null,
|
|
43
|
+
"expires": "2026-12-23T00:00:00Z"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-12-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US",
|
|
21
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "walmart",
|
|
25
|
+
"marketplace": "US",
|
|
26
|
+
"seller_id": "101234567"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "web",
|
|
30
|
+
"domain": "retailer.example"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"scope": {
|
|
34
|
+
"territories": [
|
|
35
|
+
"US",
|
|
36
|
+
"CA"
|
|
37
|
+
],
|
|
38
|
+
"product_lines": [
|
|
39
|
+
"all"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"proposed_by": null,
|
|
43
|
+
"expires": "2026-12-22T00:00:00Z"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "auth_01J9X2",
|
|
47
|
+
"retailer": {
|
|
48
|
+
"name": "Example Retail LLC",
|
|
49
|
+
"entity_id": "ent_7Q4M"
|
|
50
|
+
},
|
|
51
|
+
"channels": [
|
|
52
|
+
{
|
|
53
|
+
"type": "amazon",
|
|
54
|
+
"marketplace": "US",
|
|
55
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "walmart",
|
|
59
|
+
"marketplace": "US",
|
|
60
|
+
"seller_id": "101234567"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "web",
|
|
64
|
+
"domain": "retailer.example"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"scope": {
|
|
68
|
+
"territories": [
|
|
69
|
+
"US",
|
|
70
|
+
"CA"
|
|
71
|
+
],
|
|
72
|
+
"product_lines": [
|
|
73
|
+
"all"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"proposed_by": null,
|
|
77
|
+
"expires": "2026-12-22T00:00:00Z"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-12-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US",
|
|
21
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "walmart",
|
|
25
|
+
"marketplace": "US",
|
|
26
|
+
"seller_id": "101234567"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "web",
|
|
30
|
+
"domain": "retailer.example"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"scope": {
|
|
34
|
+
"territories": [],
|
|
35
|
+
"product_lines": [
|
|
36
|
+
"all"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"proposed_by": null,
|
|
40
|
+
"expires": "2026-12-22T00:00:00Z"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "authorized-retailers/0.1",
|
|
3
|
+
"form": "full",
|
|
4
|
+
"brand": {
|
|
5
|
+
"name": "Example Brand",
|
|
6
|
+
"domain": "brand.example"
|
|
7
|
+
},
|
|
8
|
+
"issued": "2026-09-23T00:00:00Z",
|
|
9
|
+
"expires": "2026-09-22T00:00:00Z",
|
|
10
|
+
"authorizations": [
|
|
11
|
+
{
|
|
12
|
+
"id": "auth_01J9X2",
|
|
13
|
+
"retailer": {
|
|
14
|
+
"name": "Example Retail LLC",
|
|
15
|
+
"entity_id": "ent_7Q4M"
|
|
16
|
+
},
|
|
17
|
+
"channels": [
|
|
18
|
+
{
|
|
19
|
+
"type": "amazon",
|
|
20
|
+
"marketplace": "US",
|
|
21
|
+
"seller_id": "A1B2C3D4E5F6G7"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "walmart",
|
|
25
|
+
"marketplace": "US",
|
|
26
|
+
"seller_id": "101234567"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "web",
|
|
30
|
+
"domain": "retailer.example"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"scope": {
|
|
34
|
+
"territories": [
|
|
35
|
+
"US",
|
|
36
|
+
"CA"
|
|
37
|
+
],
|
|
38
|
+
"product_lines": [
|
|
39
|
+
"all"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"proposed_by": null,
|
|
43
|
+
"expires": "2026-09-22T00:00:00Z"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|