{"openapi":"3.1.0","info":{"title":"POST /api/v1/o2c/deals/bulk","version":"1.0.0","description":"Bulk-create deals"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/o2c/deals/bulk":{"post":{"tags":["deals"],"summary":"Bulk-create deals","description":"Create up to 250 pipeline deals in one request — the import path behind the Pipeline page's Import button, for moving an existing pipeline spreadsheet in.\n\nRows are independent: one invalid row does not reject the rest. Every row comes back in `results` with its 1-based `row` number and, on failure, the reason — so a caller fixes and re-submits only what failed. Each row needs `prospect_name` or `customer_id`, and `expected_value_cents` is in CENTS.","operationId":"bulk_create_deals_api_v1_o2c_deals_bulk_post","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":{"application/json":{"schema":{"$ref":"#/components/schemas/DealBulkCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessEnvelope_DealBulkCreateResponse_"}}}},"400":{"description":"Bad Request - Invalid input","content":{"application/json":{"example":{"detail":"Invalid request parameters"}}}},"401":{"description":"Unauthorized - Authentication required","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"example":{"detail":"Not enough permissions"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"DealBulkCreateRequest":{"properties":{"deals":{"items":{"$ref":"#/components/schemas/DealBulkRow"},"type":"array","maxItems":250,"minItems":1,"title":"Deals","description":"Deals to create (1-250)."}},"type":"object","required":["deals"],"title":"DealBulkCreateRequest","description":"Create many pipeline deals in one request.\n\nRows are independent: each is validated and inserted on its own, and one\nbad row does not reject the file. The response reports every row so the\ncaller can fix and re-submit only what failed."},"DealBulkCreateResponse":{"properties":{"created":{"type":"integer","title":"Created"},"failed":{"type":"integer","title":"Failed"},"results":{"items":{"$ref":"#/components/schemas/DealBulkRowResult"},"type":"array","title":"Results"}},"type":"object","required":["created","failed","results"],"title":"DealBulkCreateResponse","description":"Per-row outcome of a bulk import."},"DealBulkRow":{"properties":{"prospect_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Prospect Name","description":"Free-text prospect/company name, used before a real customer record exists. Required unless customer_id is set."},"customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Id","description":"Existing customer this deal is for (expansion deal). Required unless prospect_name is set."},"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name","description":"Deal name. Defaults to the prospect/customer name."},"contact_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Contact Name","description":"Sales contact."},"contact_email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Contact Email","description":"Sales contact email. Never written to the customer record -- may later become the order signer."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Validated per row; an unknown source fails that row."},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage","description":"Validated per row; an unknown stage fails that row."},"is_expansion":{"type":"boolean","title":"Is Expansion","description":"True if this is expansion revenue on an existing customer.","default":false},"expected_value_cents":{"type":"integer","minimum":0.0,"title":"Expected Value Cents","description":"Estimated deal value in cents.","default":0},"expected_hours":{"anyOf":[{"type":"number","minimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*(?:\\d{0,14}|(?=[\\d.]{1,17}0*$)\\d{0,14}\\.\\d{0,2}0*$)"},{"type":"null"}],"title":"Expected Hours","description":"Estimated hours."},"expected_term_months":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Expected Term Months","description":"Estimated contract term in months."},"expected_start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expected Start Date","description":"Estimated start date."},"expected_close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expected Close Date","description":"Estimated close date."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Free-text notes."},"owner_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Owner Name","description":"Sales rep / deal owner. Free text (reps are often not DayZero users); the pipeline's owner filter matches on it exactly."},"deal_type":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Deal Type","description":"Business-defined deal category, e.g. 'Subcontractor', 'EPC', 'Developer'. Free text -- every business slices deals differently."},"revenue_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revenue Type","description":"Validated per row; an unknown value fails that row."}},"type":"object","title":"DealBulkRow","description":"One row of a bulk import.\n\nIdentical to :class:`DealCreate` except that ``stage``, ``source`` and\n``revenue_type`` are accepted as free text here and validated per row by\nthe service instead of by the schema.\n\nThat difference is the whole point of the endpoint. Pydantic validates a\nrequest body before the handler runs, so a single typo'd stage in row 40\nof a 250-row import would 422 the *entire* request — the exact\nall-or-nothing failure a spreadsheet import exists to avoid, and the one\nmost likely to happen, since these values are hand-typed. Letting the row\nthrough means ``create_deal`` rejects it with the same message the\nsingle-create path gives, against its own row number, while the other 249\nrows commit.\n\nStructural limits (a negative value, an over-long name) are still enforced\nhere: those are inherited from DealCreate and are not per-row judgement\ncalls."},"DealBulkRowResult":{"properties":{"row":{"type":"integer","title":"Row","description":"1-based index of the row in the request."},"success":{"type":"boolean","title":"Success"},"deal_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deal Id","description":"Set when the row was created."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Deal name, for display."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Why the row failed; null on success."}},"type":"object","required":["row","success"],"title":"DealBulkRowResult","description":"Outcome of one row, positionally matched to the request."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"SuccessEnvelope_DealBulkCreateResponse_":{"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/DealBulkCreateResponse"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"SuccessEnvelope[DealBulkCreateResponse]"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Token","description":"API token authentication. Format: `Bearer dz_...`"}}},"security":[{"BearerAuth":[]}]}