{"openapi":"3.1.0","info":{"title":"POST /api/v1/credit/sandbox/partner-pulls","version":"1.0.0","description":"Register a pull of a partner's data"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/credit/sandbox/partner-pulls":{"post":{"tags":["credit"],"summary":"Register a pull of a partner's data","description":"Sandbox only. `multipart/form-data` with the pull's `manifest` (manifest.json) and every NDJSON file it names as `files`. Each file's SHA-256 is checked against the manifest (`CREDIT_681`), the files are read for real emails and phone numbers whatever the manifest says, and a pull that is not redacted is refused (`CREDIT_673`) unless `allow_unredacted` is `true` with a `reason` (`CREDIT_674`). `roles` (JSON of role to table name) says which table is `transactions`, `merchants` or `payments`; the rest are worked out from the columns, and a table that cannot be placed keeps a null role. The files are stored for 30 days. Seed from it with `pull_id` on `POST /credit/sandbox/partner-export`.","operationId":"credit_register_partner_pull","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-business-id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Business-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_credit_register_partner_pull_api_v1_credit_sandbox_partner_pulls_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessEnvelope_PartnerPullResponse_"}}}},"400":{"description":"Bad Request - Invalid input","content":{"application/json":{"example":{"error":"validation_error","message":"Validation failed: due_date: Input should be a valid date","errors":{"due_date":"Input should be a valid date"},"code":"GEN_002","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or expired token, an MCP OAuth token used against the REST API, or no access to this business","content":{"application/json":{"example":{"error":"unauthorized","message":"Authentication required","code":"AUTH_001","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"example":{"error":"forbidden","message":"You don't have permission to access this resource","code":"AUTH_002","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not Found - Resource does not exist","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found","code":"NOT_FOUND_001","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"validation_error","message":"Validation failed: amount: Input should be a valid integer","errors":{"amount":"Input should be a valid integer"},"code":"GEN_002","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"}}}}},"x-fastapi-operation-id":"credit_register_partner_pull_api_v1_credit_sandbox_partner_pulls_post"}}},"components":{"schemas":{"Body_credit_register_partner_pull_api_v1_credit_sandbox_partner_pulls_post":{"properties":{"manifest":{"type":"string","contentMediaType":"application/octet-stream","title":"Manifest","description":"The pull's manifest.json."},"files":{"anyOf":[{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array"},{"type":"null"}],"title":"Files","description":"The NDJSON files it names."},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"A name for the pull."},"roles":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Roles","description":"JSON of role to table name, e.g. {\"transactions\": \"tie-transactions\"}."},"allow_unredacted":{"type":"boolean","title":"Allow Unredacted","description":"Keep a pull that is not redacted. Needs `reason`.","default":false},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Why un-redacted data may be kept. Logged and audited."}},"type":"object","required":["manifest"],"title":"Body_credit_register_partner_pull_api_v1_credit_sandbox_partner_pulls_post"},"PartnerPullResponse":{"properties":{"id":{"type":"string","title":"Id","description":"The pull; send it as `pull_id` on the export."},"label":{"type":"string","title":"Label","description":"What the uploader called the pull."},"tables":{"items":{"$ref":"#/components/schemas/PartnerPullTable"},"type":"array","title":"Tables","description":"The files of the pull."},"redacted":{"type":"boolean","title":"Redacted","description":"True when the manifest says redaction was on and the files hold no email or phone number that looks real."},"salt_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Salt Id","description":"Names the redaction salt; never the salt."},"scanned_at":{"type":"string","format":"date-time","title":"Scanned At","description":"When the tables were scanned (UTC)."},"uploaded_by_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Uploaded By User Id","description":"Who uploaded it, when a person did."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When it was registered (UTC)."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"When the files are removed: 30 days after registering (UTC)."},"roles_set":{"type":"boolean","title":"Roles Set","description":"True when a table is the transactions file, so it can be seeded."}},"type":"object","required":["id","label","tables","redacted","scanned_at","created_at","expires_at","roles_set"],"title":"PartnerPullResponse"},"PartnerPullTable":{"properties":{"table":{"type":"string","title":"Table","description":"The DynamoDB table the file was pulled from."},"file":{"type":"string","title":"File","description":"The NDJSON file's name."},"items":{"type":"integer","title":"Items","description":"How many items the file holds."},"sha256":{"type":"string","title":"Sha256","description":"The file's SHA-256, checked on upload."},"role":{"anyOf":[{"type":"string","enum":["transactions","merchants","payments"]},{"type":"null"}],"title":"Role","description":"What the table is to the seeder: `transactions`, `merchants` or `payments`. Null when it could not be worked out: say which is which with `roles` on the export."}},"type":"object","required":["table","file","items","sha256"],"title":"PartnerPullTable"},"SuccessEnvelope_PartnerPullResponse_":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"},"data":{"anyOf":[{"$ref":"#/components/schemas/PartnerPullResponse"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"SuccessEnvelope[PartnerPullResponse]"},"ErrorResponse":{"title":"ErrorResponse","type":"object","description":"Envelope returned by every non-2xx response. Branch on `code` (stable) rather than `message` (human-readable, may change).","required":["error","message","code"],"properties":{"error":{"type":"string","description":"Error category: `validation_error`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited`, `server_error`, or `service_unavailable`."},"message":{"type":"string","description":"Human-readable explanation, safe to show to end users."},"code":{"type":"string","description":"Stable machine-readable code in `CATEGORY_NNN` form (e.g. `NOT_FOUND_006`, `AUTH_010`, `GEN_002`)."},"request_id":{"type":"string","description":"Correlation id for support requests. Echoes the `x-request-id` request header when one was supplied."},"errors":{"type":"object","additionalProperties":{"type":"string"},"description":"Field-level validation messages keyed by field name. Present on 400/422 validation failures only."}}}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Token","description":"API token authentication. Format: `Bearer dz_...`"}}},"security":[{"BearerAuth":[]}]}